The International Simutrans Forum

 

Author Topic: Patch for recursive mutexes without initializer  (Read 80 times)

0 Members and 1 Guest are viewing this topic.

Offline Kernigh

  • *
  • Posts: 7
  • Languages: EN
Patch for recursive mutexes without initializer
« on: December 04, 2018, 10:45:45 PM »
Simutrans with MULTI_THREAD = 1 requires Linux glibc (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) or macOS (PTHREAD_RECURSIVE_MUTEX_INITIALIZER), so we can't enable MULTI_THREAD = 1 on other platforms.

I modify a patch from TurfIt and attach the modified patch. It's a git diff; use patch -p1 or git apply. This patch allows me to enable MULTI_THREAD = 1 on OpenBSD/amd64, and might help other platforms, but I did not test the patch on other platforms.

I add a new type recursive_mutex_maker_t. Its constructor takes a reference to a pthread_mutex_t and makes it into a recursive mutex.

Code: [Select]
/* old way */
static pthread_mutex_t weg_calc_image_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

/* new way */
static pthread_mutex_t weg_calc_image_mutex;
static recursive_mutex_maker_t weg_cim_maker(weg_calc_image_mutex);

The new constructor tries to use the same initializer as the old code.

Code: [Select]
#if defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
// Linux glibc
#define _SIMTHREAD_R_MUTEX_I PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
#elif defined PTHREAD_RECURSIVE_MUTEX_INITIALIZER
// Mac OS X
#define _SIMTHREAD_R_MUTEX_I PTHREAD_RECURSIVE_MUTEX_INITIALIZER
#endif

struct recursive_mutex_maker_t {
#ifdef _SIMTHREAD_R_MUTEX_I
recursive_mutex_maker_t(pthread_mutex_t &mutex) {
mutex = _SIMTHREAD_R_MUTEX_I;
}
#else
recursive_mutex_maker_t(pthread_mutex_t &mutex);
#endif
};

If there is no such initializer, the constructor calls pthread_mutex_init(). This is the only way to make a recursive pthread_mutex_t in OpenBSD.

Code: [Select]
#ifndef _SIMTHREAD_R_MUTEX_I
// initialize a recursive mutex by calling pthread_mutex_init()
#include <stdexcept>


recursive_mutex_maker_t::recursive_mutex_maker_t(pthread_mutex_t &mutex) {
pthread_mutexattr_t attr;

if (pthread_mutexattr_init(&attr) != 0 ||
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0 ||
    pthread_mutex_init(&mutex, &attr) != 0 ||
    pthread_mutexattr_destroy(&attr) != 0) {
// Can't call dbg->error(), because this constructor
// may run before simu_main() calls init_logging().
throw std::runtime_error("Can't make a recursive mutex!");
}
}
#endif

I link to pthread.h from FreeBSD, Haiku, illumos, NetBSD, OpenBSD, Linux glibc, Linux musl, macOS, Windows winpthreads. All of these define PTHREAD_MUTEX_RECURSIVE. Linux glibc and Windows winpthreads define both PTHREAD_MUTEX_RECURSIVE and PTHREAD_MUTEX_RECURSIVE_NP. Linux glibc may hide the definition of PTHREAD_MUTEX_RECURSIVE. This patch doesn't try PTHREAD_MUTEX_RECURSIVE_NP, because it tries PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP on Linux glibc.

Because recursive_mutex_maker_t only has a constructor, it doesn't have its own lock and unlock methods, so the patch doesn't change the calls to pthread_mutex_lock() or pthread_mutex_unlock(). The existing calls to pthread_mutex_lock() don't check for errors, so they might not lock the mutex if an error happened. I don't want to give an uninitialized mutex to pthread_mutex_lock() -- it might cause pthread_mutex_lock() to misbehave or crash -- so this patch tries to check that pthread_mutex_init() does initialize each mutex.