Static Locking Callbacks Multithread Support

61 There are two different sets of callbacks that an application is expected to provide to safely operate in a multithreaded environment. Static locks provide a fixed number of mutexes available for OpenSSLs use. Dynamic locks allow OpenSSL to create mutexes as it needs them. OpenSSL does not currently make use of dynamic locks, but reserves the right to do so in the future. If you want your applications to continue working with a minimal amount of effort in the future, we recommend that you implement both static and dynamic locks now.

4.1.1 Static Locking Callbacks

The static locking mechanism requires the application to provide two callback functions. In addition to providing an implementation for the functions, the application must tell OpenSSL about them so that it knows to call them when appropriate. The first callback function is used either to acquire or release a lock, and is defined like this: void locking_functionint mode, int n, const char file, int line; mode Determines the action that the locking function should take. When the CRYPTO_LOCK flag is set, the lock should be acquired; otherwise, it should be released. n The number of the lock that should be acquired or released. The number is zero-based, meaning that the first lock is identified by 0. The value will never be greater than or equal to the return from the CRYPTO_num_locks function. file The name of the source file requesting the locking operation to take place. It is intended to aid in debugging and is usually supplied by the _ _FILE_ _ preprocessor macro. line The source line number requesting the locking operation to take place. Like the file argument, it is also intended to aid in debugging, and it is usually supplied by the _ _LINE_ _ preprocessor macro. The next callback function is used to get a unique identifier for the calling thread. For example, GetCurrentThreadId on Windows will do just that. For reasons that will soon become clear, it is important the value returned from this function be consistent across calls for the same thread, but different for each thread within the same process. The return value from the function should be the unique identifier. The function is defined like this: unsigned long id_functionvoid; Example 4-1 introduces two new OpenSSL library functions: CRYPTO_set_id_callback and CRYPTO_set_locking_callback . These two functions are used to tell OpenSSL about the callbacks that weve implemented for the static locking mechanism. We can either pass a pointer to a function to install a callback or NULL to remove a callback. Example 4-1. Static locking callbacks for WIN32 and POSIX threads systems int THREAD_setupvoid; int THREAD_cleanupvoid; 62 if definedWIN32 define MUTEX_TYPE HANDLE define MUTEX_SETUPx x = CreateMutexNULL, FALSE, NULL define MUTEX_CLEANUPx CloseHandlex define MUTEX_LOCKx WaitForSingleObjectx, INFINITE define MUTEX_UNLOCKx ReleaseMutexx define THREAD_ID GetCurrentThreadId elif defined_POSIX_THREADS _POSIX_THREADS is normally defined in unistd.h if pthreads are available on your platform. define MUTEX_TYPE pthread_mutex_t define MUTEX_SETUPx pthread_mutex_initx, NULL define MUTEX_CLEANUPx pthread_mutex_destroyx define MUTEX_LOCKx pthread_mutex_lockx define MUTEX_UNLOCKx pthread_mutex_unlockx define THREAD_ID pthread_self else error You must define mutex operations appropriate for your platform endif This array will store all of the mutexes available to OpenSSL. static MUTEX_TYPE mutex_buf = NULL; static void locking_functionint mode, int n, const char file, int line { if mode CRYPTO_LOCK MUTEX_LOCKmutex_buf[n]; else MUTEX_UNLOCKmutex_buf[n]; } static unsigned long id_functionvoid { return unsigned longTHREAD_ID; } int THREAD_setupvoid { int i; mutex_buf = MUTEX_TYPE mallocCRYPTO_num_locks sizeofMUTEX_TYPE; if mutex_buf return 0; for i = 0; i CRYPTO_num_locks; i++ MUTEX_SETUPmutex_buf[i]; CRYPTO_set_id_callbackid_function; CRYPTO_set_locking_callbacklocking_function; return 1; } int THREAD_cleanupvoid { int i; if mutex_buf return 0; CRYPTO_set_id_callbackNULL; 63 CRYPTO_set_locking_callbackNULL; for i = 0; i CRYPTO_num_locks; i++ MUTEX_CLEANUPmutex_buf[i]; freemutex_buf; mutex_buf = NULL; return 1; } To use these static locking functions, we need to make one function call before our program starts threads or calls OpenSSL functions, and we must call THREAD_setup , which returns 1 normally and 0 if it is unable to allocate the memory required to hold the mutexes. In our example code, we do make a potentially unsafe assumption that the initialization of each individual mutex will succeed. You may wish to add additional error handling code to your programs. Once weve called THREAD_setup and it returns successfully, we can safely make calls into OpenSSL from multiple threads. After our programs threads are finished, or if we are done using OpenSSL, we should call THREAD_cleanup to reclaim any memory used for the mutex structures.

4.1.2 Dynamic Locking Callbacks