https://www.openssl.org/docs/man1.0.2/crypto/threads.html
OpenSSL can safely be used in multi-threaded applications provided that at least two callback functions are set, locking_function and threadid_func.
根據官方文檔,爲了讓OpsnSSL是線程安全的,需要至少提供兩個 callback function:
- locking_function
- threadid_func
locking_function
locking_function(int mode, int n, const char *file, int line) is needed to perform locking on shared data structures. (Note that OpenSSL uses a number of global data structures that will be implicitly shared whenever multiple threads use OpenSSL.) Multi-threaded applications will crash at random if it is not set.
locking_function() must be able to handle up to CRYPTO_num_locks() different mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and releases it otherwise.
file and line are the file number of the function setting the lock. They can be useful for debugging.
locking_function(int mode, int n, const char *file, int line)
需要對共享的數據結構進行加鎖。(OpenSSL使用一些全局的數據結構,因此在多線程環境下會有問題。)如果沒有設置此callback function,那麼多線程OpenSLL程序會隨機意外的crash。(如果沒有設置此回調函數,你的程序也沒crash,不要認爲這麼做沒有問題,只是crash還沒有發生而已)
locking_function
必須能夠處理最多CRYPTO_num_locks()
個不同的mutex鎖,如果 mode & CRYPTO_LOCK
爲true, 它會lock 第n個鎖;否則unlock。
參數flie和line用於debug。
threadid_func
threadid_func(CRYPTO_THREADID *id) is needed to record the currently-executing thread’s identifier into id. The implementation of this callback should not fill in id directly, but should use CRYPTO_THREADID_set_numeric() if thread IDs are numeric, or CRYPTO_THREADID_set_pointer() if they are pointer-based. If the application does not register such a callback using CRYPTO_THREADID_set_callback(), then a default implementation is used - on Windows and BeOS this uses the system’s default thread identifying APIs, and on all other platforms it uses the address of errno. The latter is satisfactory for thread-safety if and only if the platform has a thread-local error number facility.
threadid_func(CRYPTO_THREADID *id)
需要將當前執行線程的identifier記錄到id
中。
一個完整的例子
https://github.com/openssl/openssl/blob/OpenSSL_1_0_2g/crypto/threads/th-lock.c
上面是OpenSSL 1.0.2g版本中的代碼,已經封裝好了兩個函數:
void CRYPTO_thread_setup(void); // 註冊回調
void CRYPTO_thread_cleanup(void); // 執行清理工作
關鍵代碼:
static pthread_mutex_t *lock_cs;
static long *lock_count;
void CRYPTO_thread_setup(void)
{
int i;
lock_cs = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count = (long *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
if (!lock_cs || !lock_count) {
/* Nothing we can do about this...void function! */
if (lock_cs)
OPENSSL_free(lock_cs);
if (lock_count)
OPENSSL_free(lock_count);
return;
}
for (i = 0; i < CRYPTO_num_locks(); i++) {
lock_count[i] = 0;
pthread_mutex_init(&(lock_cs[i]), NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)(int, int, const char*, int))pthreads_locking_callback);
}
void thread_cleanup(void)
{
int i;
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_destroy(&(lock_cs[i]));
}
OPENSSL_free(lock_cs);
OPENSSL_free(lock_count);
}
void pthreads_locking_callback(int mode, int type, char *file, int line)
{
# if 0
fprintf(stderr, "thread=%4d mode=%s lock=%s %s:%d\n",
CRYPTO_thread_id(),
(mode & CRYPTO_LOCK) ? "l" : "u",
(type & CRYPTO_READ) ? "r" : "w", file, line);
# endif
# if 0
if (CRYPTO_LOCK_SSL_CERT == type)
fprintf(stderr, "(t,m,f,l) %ld %d %s %d\n",
CRYPTO_thread_id(), mode, file, line);
# endif
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
} else {
pthread_mutex_unlock(&(lock_cs[type]));
}
}
unsigned long pthreads_thread_id(void)
{
unsigned long ret;
ret = (unsigned long)pthread_self();
return (ret);
}