Skip to content

Instantly share code, notes, and snippets.

@d3x0r
Last active June 27, 2020 00:06
Show Gist options
  • Save d3x0r/36cee9fdf9d171bbedb0677188d4ed0a to your computer and use it in GitHub Desktop.
Save d3x0r/36cee9fdf9d171bbedb0677188d4ed0a to your computer and use it in GitHub Desktop.
pthread_once emulation...
// best version
// would like to do CreateEvent in static initializer; but that's not a constant expression.
#define PTHREAD_ONCE_INIT { 0, 0 }
struct pthread_once {
HANDLE event;
volatile LONG inited;
};
typedef struct pthread_once pthread_once_t;
static inline int
pthread_once(pthread_once_t *once, void (*cb) (void) ) {
if( !InterlockedExchange( &once->inited, 1 ) ) {
once->event = CreateEvent( NULL, TRUE, FALSE, NULL );
cb();
SetEvent( once->event );
}
while( !once->event ) Sleep( 0 );
DWORD rc = WaitForSingleObject( once->event, INFINITE );
if( rc == WAIT_OBJECT_0 )
return 0;
return 1;
}
//-------------------------------------------------------------------
// interlocked-exchange/spin-wait version
// modifies pthread_once and initializer
struct pthread_once {
volatile LONG inited;
volatile LONG initDone;
};
typedef struct pthread_once pthread_once_t;
static inline BOOL CALLBACK
_pthread_once_win32_cb(PINIT_ONCE once, PVOID param, PVOID *context)
{
void (*cb) (void) = param;
cb();
return TRUE;
}
static inline void wait( int *on ) {
while( !on[0] ) Sleep(0);
}
#define pthread_once(o,cb) ( \
(( !InterlockedExchange( &((o)[0]).inited, 1 ) )? \
(cb(), \
((o)[0]).initDone = 1):0), \
wait( &((o)[0]).initDone ),0 \
)
//------------------------------------------------------
// simple one-time-runner macro; static is not allowed in expressions :(
#define pthread_once(o,cb) ( \
static int inited, \
static volatile int initDone, \
( !InterlockedExchange( &inited, 1 ) )? \
(cb(), \
initDone = 1):0 \
wait( &initDone ),0 \
)
//-----------------------------------------------------------
// original method - non XP compatible.
struct pthread_once {
INIT_ONCE once;
};
typedef struct pthread_once pthread_once_t;
static inline BOOL CALLBACK
_pthread_once_win32_cb(PINIT_ONCE once, PVOID param, PVOID *context)
{
void (*cb) (void) = param;
cb();
return TRUE;
}
static inline int
pthread_once(pthread_once_t *once, void (*cb) (void))
{
BOOL rc = InitOnceExecuteOnce(&once->once, _pthread_once_win32_cb, cb, NULL);
if (rc == 0)
return -1;
else
return 0;
}
@matheustavares
Copy link

Thanks for sharing this gist. Is the cast at line 78 allowed? I mean, can't data pointers and function pointers have different sizes in some architectures?

@d3x0r
Copy link
Author

d3x0r commented Jun 26, 2020

Well; as a consolation, one would expect code pointers to be less than or equal to data pointers (?)
A compiler with an issue like that would generate a warning probably on that line... casting data pointer to function pointer; but this is windows, so I don't really expect asymmetry?

@matheustavares
Copy link

Got it. I'm not very familiar with Windows, so I wasn't sure if that could even be an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment