
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <assert.h>

#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/x509v3.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"

/******************* Pyhon core emuration ***************************/

#define PyThread_get_thread_ident pthread_self

typedef sem_t *PyThread_type_lock;

static PyThread_type_lock PyThread_allocate_lock()
{
    sem_t *sem;

    int ret;

    sem = (sem_t *)malloc(sizeof(sem_t));

    assert(sem);

    ret = sem_init(sem, 0, 1);

    assert(ret == 0);

    return sem;
}

static void PyThread_free_lock(PyThread_type_lock sem)
{
    int ret;

    ret = sem_destroy(sem);

    assert(ret == 0);

    free(sem);
}

static int PyThread_acquire_lock(PyThread_type_lock sem, int waitflag)
{
    int ret;

    assert(waitflag == 1);

    ret = sem_wait(sem);

    assert(ret == 0);

    return 1;
}

static void PyThread_release_lock(PyThread_type_lock sem)
{
    int ret;

    ret = sem_post(sem);

    assert(ret == 0);
}

/******************* Modules/_ssl.c ***************************/

static unsigned int _ssl_locks_count = 0;

static PyThread_type_lock *_ssl_locks = NULL;

static unsigned long _ssl_thread_id_function (void) {
	return PyThread_get_thread_ident();
}

static void _ssl_thread_locking_function (int mode, int n, const char *file, int line) {

	if ((_ssl_locks == NULL) ||
	    (n < 0) || ((unsigned)n >= _ssl_locks_count))
		return;

	printf("======> %s %d\n", file, line); /* XXX */
	if (mode & CRYPTO_LOCK) {
		printf("----> acquire enter %u %lx\n", n, PyThread_get_thread_ident());
		PyThread_acquire_lock(_ssl_locks[n], 1);
		printf("----> acquire leave %u %lx\n", n, PyThread_get_thread_ident());
        } else {
		printf("----> release enter %u %lx\n", n, PyThread_get_thread_ident());
		PyThread_release_lock(_ssl_locks[n]);
		printf("----> release leave %u %lx\n", n, PyThread_get_thread_ident());
	}
}

static int _setup_ssl_threads(void) {

	unsigned int i;

	if (_ssl_locks == NULL) {
		_ssl_locks_count = CRYPTO_num_locks();
		_ssl_locks = (PyThread_type_lock *)
			malloc(sizeof(PyThread_type_lock) * _ssl_locks_count);
		if (_ssl_locks == NULL)
			return 0;
		memset(_ssl_locks, 0, sizeof(PyThread_type_lock) * _ssl_locks_count);
		for (i = 0;  i < _ssl_locks_count;  i++) {
			_ssl_locks[i] = PyThread_allocate_lock();
			if (_ssl_locks[i] == NULL) {
				int j;
				for (j = 0;  j < i;  j++) {
					PyThread_free_lock(_ssl_locks[j]);
				}
				free(_ssl_locks);
				return 0;
			}
		}
		CRYPTO_set_locking_callback(_ssl_thread_locking_function);
		CRYPTO_set_id_callback(_ssl_thread_id_function);
	}
	return 1;
}

/******************* main.c ***************************/

static volatile int _teminated_thread_count = 0;

static void *thread_main(void *arg)
{
    sleep(1);

    ++_teminated_thread_count;
}

#define _thread_count 10

static pthread_t _threads[_thread_count];

#define USE_DETACH /* abort */

int main()
{
    int ret, i;

    ret = _setup_ssl_threads();

    assert(ret == 1);

    for (i = 0; i < _thread_count; ++i)
    {
        ret = pthread_create(&_threads[i], NULL, thread_main, NULL);

        assert(ret == 0);

#ifdef USE_DETACH
        pthread_detach(_threads[i]);
#endif
    }

#ifdef USE_DETACH
    while (_teminated_thread_count < _thread_count)
    {
        sleep(1);
    }
#else
    for (i = 0; i < _thread_count; ++i)
    {
        ret = pthread_join(_threads[i], NULL);

        assert(ret == 0);
    }
#endif
}

