Android NDK: From Elementary to Expert Episode 19

来源:互联网 发布:科技部办公厅关于优化 编辑:程序博客网 时间:2024/06/06 07:04

Synchronizing POSIX Threads Using Semaphores

Unlike the other POSIX functions, the POSIX semaphores are declared in a different header file, the semaphore.h.

#include <semaphore.h>

The POSIX semaphores are exposed to native code through the sem_t data type. The POSIX Semaphore API provides a set of functions for interacting with the semaphores from the native code. Prior being used, the semaphore variables should be initialized first.

Initializing Semaphores

The POSIX Semaphore API provides the sem_init function to initialize the semaphore variables.

extern int sem_init(sem_t* sem, int pshared, unsigned int value);

It takes three arguments: a pointer to the semaphore variable that will be initialized, the share flag, and its initial value. On success, the function returns zero; otherwise −1 is returned.

Locking Semaphores

Once the semaphore is properly initialized, threads can use the sem_wait function to decrease the number of the semaphore.

extern int sem_wait(sem_t* sem);

The function takes a pointer to the semaphore variable. If semaphore’s value is greater than zero, the locking succeeds and the value of the semaphore gets decremented accordingly. If the value of the semaphore is zero, then the calling thread gets suspended until the semaphore value gets incremented by another thread through unlocking it. On success, the function returns zero; otherwise −1 is returned.

Unlocking Semaphores

Upon finishing executing the critical code section, the thread can unlock the semaphore using the sem_post function.

extern int sem_post(sem_t* sem);

When the semaphore gets unlocked by the sem_post function, its value gets incremented by one. Scheduling policy decides which thread waiting on the semaphore gets executed next. On success, the function returns zero; otherwise −1 is returned.

Destroying Semaphores

Once the semaphore is no longer needed, it can be destroyed through the sem_destory function.

extern int sem_destroy(sem_t* sem);

The function takes a pointer to the semaphore variables that will be destroyed. Destroying a semaphore that another thread is currently blocked on may result in undefined behavior. On success, the function returns zero; otherwise −1 is returned.

POSIX Thread Scheduling Strategy

The POSIX Thread specification requires a set of scheduling strategies to be implemented. The most frequently used scheduling policies are the following:

  • SCHED_FIFO: The first in, first out scheduling policy orders the list of threads based on the time the thread has been on the list. Based on its priority, the thread can also move within the thread list.
  • SCHED_RR: the round-robin scheduling policy is identical to the SCHED_FIFO scheduling policy with the addition of limiting the duration of the thread execution to prevent any thread monopolizing the available CPU cycles.

sched.h header file. The scheduling strategy sched_policy field of the thread attributes structure pthread_attr_tpthread_create function, or during runtime through the function.

int pthread_setschedparam(pthread_t thid, int poilcy, struct sched_param const* param);

Synchronizing POSIX Threads

The POSIX Thread API also provides synchronization functionality. In this chapter, you will mainly focus on the two most frequently used synchronization mechanisms offered by the POSIX Threads:

  • Mutexes allow mutual exclusion in the code where specific portions of the code do not execute at the same time.
  • Semaphores control access to a resource based on a defined number of available resources. If no resource is available, the calling thread simply waits on the semaphore until a resource becomes available.

Synchronizing POSIX Threads using Mutexes

POSIX Thread API exposes mutexes to the native code through the pthread_mutex_t data type. The POSIX Thread API provides a set of functions for interacting with mutexes from the native code. Prior to being used, the mutex variables should be initialized first.

Initializing Mutexes

The POSIX Thread API provides two ways of initializing the mutexes: pthread_mutex_init function and the PTHREAD_MUTEX_INITIALIZER macro. The pthread_mutex_init function can be used to initialize the mutexes.

int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* attr);

The pthread_mutex_init function takes two arguments, a pointer to the mutex variable to initialize and a pointer to the pthread_mutextattr_t structure defining the attributes for the mutex. If the second argument is set to NULL, the default attributes gets used. If the default attributes are enough, instead of the pthread_mutex_init function, the PTHREAD_MUTEX_INITIALIZER macro is more appropriate.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Upon successful initialization, the state of the mutex becomes initialized and unlocked, and the function returns zero; otherwise it returns the error code.

Locking Mutexes

The pthread_mutex_lock function can be used to gain mutual exclusion by locking an already initialized mutex.

int pthread_mutex_lock(pthread_mutex_t* mutex);

The function takes a pointer to the mutex variable. If the mutex is already being locked, the calling thread gets suspended until the mutex becomes available. In case of success, the function returns zero; otherwise it returns the error code.

Unlocking Mutexes

Upon completing executing the critical code section, the mutex can be unlocked using the pthread_mutex_unlock function.
int pthread_mutex_unlock(pthread_mutex_t* mutex);
The function takes a pointer to the mutex variable and unlocks it. The scheduling policy decides which thread waiting on the mutex gets executed next. In case of success, the function returns zero; otherwise it returns the error code.

int pthread_mutex_destroy

Using POSIX Threads in Native Code

The POSIX Thread API is declared through the pthread.h header file. In order to use POSIX Threads in native code, this header file needs to be included first.

#include <pthread.h>

The Android implementation of POSIX Threads is part of the Bionic standard C standard library. Unlike other platforms, it does not require linking of any additional library during compile-time.
Creating Threads using pthread_create
The POSIX Threads are created through the pthread_create function.

int pthread_create(pthread_t* thread,    pthread_attr_t const* attr,void* (*start_routine)(void*),void* arg);

The function takes the following arguments:

  • Pointer to a thread_t type variable that will be used by the function to return the handle for the new thread.
  • Attributes for the new thread in the form of a pointer to a pthread_attr_t
    structure. Stack base, stack size, guard size, scheduling policy, and scheduling
    priority for the new thread can be specified through the attributes. You will learn
    about some of these attributes later in the chapter. It can be NULL if the default attributes are going to be used.
  • A function pointer to the start routine for the thread. The start routine function
    signature should look like the following:
    void* start_rountine (void* args)
    The start routine takes the thread arguments as a void pointer, and it returns a result as a void pointer.
  • Any arguments should be passed to the start routine when the thread gets executed in the form of a void pointer. It can be NULL if not arguments needs to be passed.

pthread_create function returns zero; otherwise it returns an error code.

Return Result from POSIX Threads

Threads can return a result back when they are terminating. This is achieved through the void pointer that is returned from the thread start routine. In the previous example, the Java_com_apress_threads_ MainActivity_posixThreads function is designed to return immediately after executing the threads. The function can be modified to wait for threads to finish their work and return. A function can wait for a thread to terminate by using the pthread_join function.

int pthread_join(pthread_t thread, void** ret_val);

The pthread_join function takes the following arguments:

  • Thread handle that is returned from the pthread_create function for the target thread.
  • Pointer to a void pointer for obtaining the returned result from the start routine.

It suspends the execution of the calling thread until the target thread terminates. If the ret_val is not NULL, the function will set the value of ret_val pointer to the result returned from the start routine. In case of success, pthread_join function returns zero; otherwise it returns the error code.