Programming


C: Full example of pthread_cond_timedwait() 2

The following code has two threads.
The main thread spawns a pthread and then blocks on a condition waiting for a signal from the pthread.
The pthread will perform its task and then signal the main thread.
Once the main thread receives its signal, it will join the pthread and terminate.

[download id=”2713″]


#include <stdio.h>
#include <sys/types.h>
#include <pthread.h>
#include <asm/errno.h>

#define MAX_WAIT_TIME_IN_SECONDS (6)

struct thread_info_t
{
    // Used to identify a thread.
    pthread_t thread_id;
    // A condition is a synchronization device that allows threads to suspend execution and relinquish the processors until some predicate on shared data is satisfied.
    // The basic operations on conditions are: signal the condition (when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.
    pthread_cond_t condition;
    // A mutex is a MUTual EXclusion device, and is useful for protecting shared data structures from concurrent modifications, and implementing critical sections and monitors.
    // A mutex has two possible states: unlocked (not owned by any thread), and locked (owned by one thread).
    // A mutex can never be owned by two different threads simultaneously.
    // A thread attempting to lock a mutex that is already locked by another thread is suspended until the owning thread unlocks the mutex first.
    pthread_mutex_t mutex;
};

void error_pthread_mutex_unlock(const int unlock_rv)
{
    fprintf(stderr, "Failed to unlock mutex.\n");
    switch (unlock_rv)
    {
        case EINVAL:
            fprintf(stderr, "The value specified by mutex does not refer to an initialized mutex object.\n");
            break;
        case EAGAIN:
            fprintf(stderr, "The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.\n");
            break;
        case EPERM:
            fprintf(stderr, "The current thread does not own the mutex.\n");
            break;
        default:
            break;
    }
}

void error_pthread_mutex_lock(const int lock_rv)
{
    fprintf(stderr, "Failed to lock mutex.\n");
    switch (lock_rv)
    {
        case EINVAL:
            fprintf(stderr, "The value specified by mutex does not refer to an initialized mutex object or the mutex was created with the protocol attribute having the value PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling.\n");
            break;
        case EAGAIN:
            fprintf(stderr, "The mutex could not be acquired because the maximum number of recursive locks for mutex has been exceeded.\n");
            break;
        case EDEADLK:
            fprintf(stderr, "A deadlock condition was detected or the current thread already owns the mutex.\n");
            break;
        default:
            break;
    }
}

void error_pthread_cond_signal(const int signal_rv)
{
    fprintf(stderr, "Could not signal.\n");
    if (signal_rv == EINVAL)
    {
        fprintf(stderr, "The value cond does not refer to an initialised condition variable.\n");
    }
}

void error_pthread_setcanceltype(const int setcanceltype_rv)
{
    fprintf(stderr, "Could not change cancelability type of thread.\n");
    if (setcanceltype_rv == EINVAL)
    {
        fprintf(stderr, "Invalid value for type.\n");
    }
}

void error_pthread_create(const int create_rv)
{
    fprintf(stderr, "Could not create thread.\n");
    switch (create_rv)
    {
        case EAGAIN:
            fprintf(stderr, "Insufficient resources to create another thread or a system-imposed limit on the number of threads was encountered.\n");
            break;
        case EINVAL:
            fprintf(stderr, "Invalid settings in attr.\n");
            break;
        case EPERM:
            fprintf(stderr, "No permission to set the scheduling policy and parameters specified in attr.\n");
            break;
        default:
            break;
    }
}

void error_pthread_cond_timedwait(const int timed_wait_rv)
{
    fprintf(stderr, "Conditional timed wait, failed.\n");
    switch (timed_wait_rv)
    {
        case ETIMEDOUT:
            fprintf(stderr, "The time specified by abstime to pthread_cond_timedwait() has passed.\n");
            break;
        case EINVAL:
            fprintf(stderr, "The value specified by abstime, cond or mutex is invalid.\n");
            break;
        case EPERM:
            fprintf(stderr, "The mutex was not owned by the current thread at the time of the call.\n");
            break;
        default:
            break;
    }
}

void error_pthread_join(const int join_rv)
{

    fprintf(stderr, "Could not join thread.\n");
    switch (join_rv)
    {
        case EINVAL:
            fprintf(stderr, "The implementation has detected that the value specified by thread does not refer to a joinable thread.\n");
            break;
        case ESRCH:
            fprintf(stderr, "No thread could be found corresponding to that specified by the given thread ID.\n");
            break;
        case EDEADLK:
            fprintf(stderr, "A deadlock was detected or the value of thread specifies the calling thread.\n");
            break;
        default:
            break;
    }
}

void error_clock_gettime(const int gettime_rv)
{
    fprintf(stderr, "Could not get time from clock.\n");
    switch (gettime_rv)
    {
        case EFAULT:
            fprintf(stderr, "tp points outside the accessible address space.\n");
            break;
        case EINVAL:
            fprintf(stderr, "The clk_id specified is not supported on this system.\n");
            break;
        case EPERM:
            fprintf(stderr, "clock_settime() does not have permission to set the clock indicated.\n");
            break;
        default:
            break;
    }
}

// This is the thread that will be called by pthread_create() and it will be executed by the new thread.
void *worker_thread(void *data)
{
    // We know that the input data pointer is pointing to a thread_info_t so we are casting it to the right type.
    struct thread_info_t *thread_info = (struct thread_info_t *) data;

    // We block this thread trying to lock the mutex, this way we will make sure that the parent thread had enough time to call pthread_cond_timedwait().
    // When the parent thread calls pthread_cond_timedwait() it will unlock the mutex and this thread will be able to proceed.
    const int lock_rv = pthread_mutex_lock(&(thread_info->mutex));
    if (lock_rv)
    {
        error_pthread_mutex_lock(lock_rv);
    }

    int oldtype;
    // The pthread_setcanceltype() sets the cancelability type of the calling thread to the value given in type.
    // The previous cancelability type of the thread is returned in the buffer pointed to by oldtype.
    // The argument PTHREAD_CANCEL_ASYNCHRONOUS means that the thread can be canceled at any time.
    const int setcanceltype_rv = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
    if (setcanceltype_rv)
    {
        error_pthread_setcanceltype(setcanceltype_rv);
    }

    // TODO: This is the place you should implement the functionality that is needed for this thread

    // The pthread_cond_signal() call unblocks at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond).
    const int signal_rv = pthread_cond_signal(&(thread_info->condition));
    if (signal_rv)
    {
        error_pthread_cond_signal(signal_rv);
    }

    // The pthread_mutex_unlock() function shall release the mutex object referenced by mutex.
    const int unlock_rv = pthread_mutex_unlock(&(thread_info->mutex));
    if (unlock_rv)
    {
        error_pthread_mutex_unlock(unlock_rv);
    }
    return NULL;
}

int main()
{
    struct thread_info_t thread_info;
    pthread_cond_init(&thread_info.condition, NULL);
    pthread_mutex_init(&thread_info.mutex, NULL);

    const int lock_rv = pthread_mutex_lock(&thread_info.mutex);
    if (lock_rv)
    {
        error_pthread_mutex_lock(lock_rv);
    }

    const int create_rv = pthread_create(&(thread_info.thread_id), NULL, &worker_thread, (void *) &thread_info);
    if (create_rv)
    {
        error_pthread_create(create_rv);
        const int unlock_rv = pthread_mutex_unlock(&thread_info.mutex);
        if (unlock_rv)
        {
            error_pthread_mutex_unlock(unlock_rv);
        }
    }
    else
    {
        // timespec is a structure holding an interval broken down into seconds and nanoseconds.
        struct timespec max_wait = {0, 0};

        // The clock_gettime system call has higher precision than its successor the gettimeofday().
        // It has the ability to request specific clocks using the clock id.
        // It fills in a timespec structure with the seconds and nanosecond count of the time since the Epoch (00:00 1 January, 1970 UTC).
        // CLOCK_REALTIME argument represents a system-wide real-time clock. This clock is supported by all implementations and returns the number of seconds and nanoseconds since the Epoch.
        const int gettime_rv = clock_gettime(CLOCK_REALTIME, &max_wait);
        if (gettime_rv)
        {
            error_clock_gettime(gettime_rv);
        }
        max_wait.tv_sec += MAX_WAIT_TIME_IN_SECONDS;

        // The pthread_cond_timedwait() function blocks on a condition variable.
        // It must be called with a mutex locked by the calling thread or undefined behavior results will occur.
        // This function atomically releases the mutex and causes the calling thread to block on the condition variable cond;
        // atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable".
        // That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_broadcast() or pthread_cond_signal() in that thread shall behave as if it were issued after the about-to-block thread has blocked.
        const int timed_wait_rv = pthread_cond_timedwait(&thread_info.condition, &thread_info.mutex, &max_wait);
        if (timed_wait_rv)
        {
            error_pthread_cond_timedwait(timed_wait_rv);
        }

        // The pthread_join() function suspends execution of the calling thread until the target thread terminates, unless the target thread has already terminated.
        const int join_rv = pthread_join(thread_info.thread_id, NULL);
        if (join_rv)
        {
            error_pthread_join(join_rv);
        }
    }
    return 0;
}

[download id=”2713″]


C Bit Fields: Full example code

The following two examples demonstrate the use of bit fields to reduce memory consumption of certain applications.

In the first example we create a compressed ‘bit’ struct and on the second we create a weird struct representation for bytes to show that the size of that struct is significantly less that the original one.

First example: Using bit fields to create a ‘bit’ structure

[download id=”2693″]

#include <stdio.h>

// Weird structure to represent a 'bit'
typedef struct {
    unsigned char value;
} bit;

// 'bit' structure using bit fields
typedef struct {
    unsigned char value : 1;
} bit_bit_field;

int main( ) {

    printf( "Memory size occupied by 'bit' struct : %zu bytes\n", sizeof(bit));
    printf( "Memory size occupied by 'bit_bit_field' struct : %zu bytes\n", sizeof(bit_bit_field));

    bit bits[8];
    bit_bit_field bits_bit_field[8];

    printf( "Memory size occupied by 'bits' array : %zu bytes\n", sizeof(bits));
    printf( "Memory size occupied by 'bits_bit_field' array : %zu bytes\n", sizeof(bits_bit_field));

    // Setting the value of the first 'bit' and then printing it.
    // We will use various values for this test to show that when you set a value to a bit field
    // that is greater that the allowed size it will fill it using the last bits only.
    // It will not spill data though to neighbouring 'bits'.
    unsigned char value;
    for (value = 0; value < 4; value++)
    {
        printf("Input Value: %d\n", value);
        int bits_i;
        const int bits_length = (sizeof(bits) / sizeof(bit));
        for (bits_i = 0; bits_i < bits_length; bits_i++)
        {
            if (bits_i % 2)
            {
                bits[bits_i].value = 0;
            }
            else
            {
                bits[bits_i].value = value;
            }
            printf("%d", bits[bits_i].value);
        }
        printf("\n");

        int bits_bit_field_i;
        const int bits_bit_field_length = (sizeof(bits_bit_field) / sizeof(bit_bit_field));
        for (bits_bit_field_i = 0; bits_bit_field_i < bits_bit_field_length; bits_bit_field_i++)
        {
            if (bits_bit_field_i % 2)
            {
                bits_bit_field[bits_bit_field_i].value = 0;
            }
            else
            {
                bits_bit_field[bits_bit_field_i].value = value;
            }
            printf("%d", bits_bit_field[bits_bit_field_i].value);
        }
        printf("\n");
    }
    return 0;
}

[download id=”2693″]

Execution output

Memory size occupied by 'bit' struct : 1 bytes
Memory size occupied by 'bit_bit_field' struct : 1 bytes
Memory size occupied by 'bits' array : 8 bytes
Memory size occupied by 'bits_bit_field' array : 8 bytes
Input Value: 0
00000000
00000000
Input Value: 1
10101010
10101010
Input Value: 2
20202020
00000000
Input Value: 3
30303030
10101010

Second example: Using bit fields to create a ‘byte’ structure where each ‘bit’ is another named member

[download id=”2692″]

#include <stdio.h>

// Weird structure to represent a 'byte'
typedef struct {
    unsigned char bit_0;
    unsigned char bit_1;
    unsigned char bit_2;
    unsigned char bit_3;
    unsigned char bit_4;
    unsigned char bit_5;
    unsigned char bit_6;
    unsigned char bit_7;
} byte;

// 'byte' structure using bit fields
// Unfortunately we cannot declare an array where the values are bit fields,
// so we have to declare each member separately.
// We instruct the compiler to use only one bit per element.
typedef struct {
    unsigned char bit_0 : 1;
    unsigned char bit_1 : 1;
    unsigned char bit_2 : 1;
    unsigned char bit_3 : 1;
    unsigned char bit_4 : 1;
    unsigned char bit_5 : 1;
    unsigned char bit_6 : 1;
    unsigned char bit_7 : 1;
} byte_bit_field;

int main( ) {

    printf( "Memory size occupied by 'byte' struct : %zu bytes\n", sizeof(byte));
    printf( "Memory size occupied by 'byte_bit_field' struct : %zu bytes\n", sizeof(byte_bit_field));

    byte bytes[8];
    byte_bit_field bytes_bit_field[8];

    printf( "Memory size occupied by 'bytes' array : %zu bytes\n", sizeof(bytes));
    printf( "Memory size occupied by 'bytes_bit_field' array : %zu bytes\n", sizeof(bytes_bit_field));

    // Setting the value of the first 'bit' and then printing it.
    // We will use various values for this test to show that when you set a value to a bit field
    // that is greater that the allowed size it will fill it using the last bits only.
    // It will not spill data though to neighbouring 'bits'.
    unsigned char value;
    for (value = 0; value < 4; value++)
    {
        printf("Input Value: %d\n", value);
        int bytes_i;
        const int bytes_length = (sizeof(bytes) / sizeof(byte));
        for (bytes_i = 0; bytes_i < bytes_length; bytes_i++)
        {
            if (bytes_i % 2)
            {
                bytes[bytes_i].bit_3 = 0;
            }
            else
            {
                bytes[bytes_i].bit_3 = value;
            }
            printf(" %d  ", bytes[bytes_i].bit_3);
        }
        printf("\n");

        int bytes_bit_field_i;
        const int bytes_bit_field_length = (sizeof(bytes_bit_field) / sizeof(byte_bit_field));
        for (bytes_bit_field_i = 0; bytes_bit_field_i < bytes_bit_field_length; bytes_bit_field_i++)
        {
            if (bytes_bit_field_i % 2)
            {
                bytes_bit_field[bytes_bit_field_i].bit_3 = 0;
            }
            else
            {
                bytes_bit_field[bytes_bit_field_i].bit_3 = value;
            }
            printf("%d%d%d ",
                   bytes_bit_field[bytes_bit_field_i].bit_2,
                   bytes_bit_field[bytes_bit_field_i].bit_3,
                   bytes_bit_field[bytes_bit_field_i].bit_4);
        }
        printf("\n");
    }
    return 0;
}

[download id=”2692″]

Execution output

Memory size occupied by 'byte' struct : 8 bytes
Memory size occupied by 'byte_bit_field' struct : 1 bytes
Memory size occupied by 'bytes' array : 64 bytes
Memory size occupied by 'bytes_bit_field' array : 8 bytes
Input Value: 0
 0   0   0   0   0   0   0   0  
000 000 000 000 000 000 000 000 
Input Value: 1
 1   0   1   0   1   0   1   0  
010 000 010 000 010 000 010 000 
Input Value: 2
 2   0   2   0   2   0   2   0  
000 000 000 000 000 000 000 000 
Input Value: 3
 3   0   3   0   3   0   3   0  
010 000 010 000 010 000 010 000

asn1c: undefined reference to `SET_OF_encode_uper’

The following post is for the https://lionet.info/asn1c/ (repository: https://github.com/vlm/asn1c/)

When trying to link an ASN.1 structure that uses a SET OF, with support for Unaligned Packed Encoding Rules (UPER), we get the following error: undefined reference to 'SET_OF_encode_uper'.

Unfortunately, there is currently no solution for this problem, so we replaced the SET OF with a SEQUENCE OF.

The SEQUENCE OF type is the list (array) of simple or constructed types. The SET OF type models the bag of structures. It resembles the SEQUENCE OF type, but the order is not important: i.e. the elements may arrive in the order which is not necessarily the same as the in-memory order on the remote machines.

— From http://lionet.info/asn1c/asn1c-usage.html

Original (problematic) code

Elements ::= SEQUENCE
{
    property INTEGER,
    objects SET OF object
}

Updated (working) code

Elements ::= SEQUENCE
{
    property INTEGER,
    objects SEQUENCE OF object
}

 


asn1c: How do I know how big a buffer to allocate before using ‘uper_encode_to_new_buffer’?

The following post is for the https://lionet.info/asn1c/ (repository: https://github.com/vlm/asn1c/)

There is no need to compute the space needed.

If you pass the address to a pointer that is NULL pointer as the last parameter of uper_encode_to_new_buffer(asn_TYPE_descriptor_t *td, asn_per_constraints_t *constraints, void *sptr, void **buffer_r) , then it will allocate by itself the required space.


void *buffer = NULL;
asn_per_constraints_s *constraints = NULL;
ssize_t ec = uper_encode_to_new_buffer(&asn_DEF_Image, constraints, image, &buffer);