|
Message-ID: <CAOZ3c1rUN24Arx5iy+Zm-4gYajfBthQsgL9q-iiNQpAo1aZL8w@mail.gmail.com> Date: Mon, 21 Feb 2022 11:36:06 +0000 From: Lee Shallis <gb2985@...il.com> To: musl@...ts.openwall.com Subject: Suggestion for thread safety First I'll start with the code snippets I've copied from my own code: /* The error locks only work for conforming code, anything that doesn't will * corrupt the result if it tries to do something that would need them */ typedef const char* LOCK; typedef struct _GRIP { LOCK _; LOCK *_lock; struct _GRIP *_grip; } GRIP; BASIC void LockStdErr( LOCK *ud ); BASIC void LockSysErr( LOCK *ud ); BASIC void LockSiData( LOCK **shared, LOCK *ud ); BASIC void LockMiData( GRIP *shared, GRIP *ud ); BASIC dint FreeStdErr( LOCK *ud ); BASIC dint FreeSysErr( LOCK *ud ); BASIC dint FreeSiData( LOCK **shared, LOCK *ud ); BASIC void FreeMiData( GRIP *shared, GRIP *ud ); #define LockErrors( ud ) LockStdErr( ud ); LockSysErr( ud ) #define FreeErrors( ud ) FreeSysErr( ud ); FreeStdErr( ud ) #define DO_LOCK( X ) { LOCK _lock = ""; X } #define DO_GRIP( X ) { GRIP _grip = {NULL}; X } #define LOCK_SIDATA( SID, X ) \ DO_LOCK( LockSiData( SID, &_lock ); X *SID = NULL; ) #define LOCK_MIDATA( MID, X ) \ DO_GRIP( LockMiData( MID, &_grip ); X FreeMiData( MID, &_grip ); ) #define LOCK_STDERR( X ) \ DO_LOCK( LockStdErr( &_lock ); X FreeStdErr( &_lock ); ) #define LOCK_SYSERR( X ) \ DO_LOCK( LockSysErr( &_lock ); X FreeSysErr( &_lock ); ) #define LOCK_ERRORS( X ) \ DO_LOCK( LockErrors( &_lock ); X FreeErrors( &_lock ); ) #define LOCK_SIDATA_AND_ERRORS( SID, X ) \ DO_LOCK \ ( \ LockSiData( SID, &_lock ); \ LockErrors( &_lock ); \ X \ FreeErrors( &_lock ); \ *SID = NULL; \ ) ... SHARED_EXP void NoPause() {} LOCK *stderr_lock = NULL; LOCK *syserr_lock = NULL; allot_cb AllotCB = Allot; pause_cb pauseCB = NoPause; void LockStdErr( LOCK *ud ) { LockSiData( &stderr_lock, ud ); } dint FreeStdErr( LOCK *ud ) { return FreeSiData( &stderr_lock, ud ); } void LockSysErr( LOCK *ud ) { LockSiData( &syserr_lock, ud ); } dint FreeSysErr( LOCK *ud ) { return FreeSiData( &syserr_lock, ud ); } ... SHARED_EXP dint Allot( void *ud, void **data, size_t size ) { (void)ud; if ( size ) { dint err; LOCK_ERRORS ( errno = 0; *data = *data ? realloc( *data, size ) : malloc( size ); err = *data ? 0 : errno; ); return err; } else if ( *data ) { free( *data ); *data = NULL; } return 0; } SHARED_EXP void LockSiData( LOCK **shared, LOCK *ptr ) { while ( *shared != ptr ) { if ( !(*shared) ) *shared = ptr; pauseCB( ptr ); } } SHARED_EXP dint FreeSiData( LOCK **shared, LOCK *ud ) { if ( *shared == ud ) { *shared = NULL; return 0; } return EPERM; } /* These 2 are untested */ SHARED_EXP void LockMiData( GRIP *shared, GRIP *ud ) { while ( shared->_grip != ud ) { while ( shared->_grip ) shared = shared->_grip; shared->_grip = ud; pauseCB(); } } SHARED_EXP void FreeMiData( GRIP *shared, GRIP *ud ) { LOCK _ = NULL; LockSiData( &(shared->_lock), &_ ); LockSiData( &(ud->_lock), &_ ); while ( shared->_grip && shared->_grip != ud ) shared = shared->_grip; if ( shared->_grip == ud ) { shared->_grip = ud->_grip; ud->_grip = NULL; } (void)FreeSiData( &(ud->_lock), &_ ); (void)FreeSiData( &(shared->_lock), &_ ); } Take what you will of the above, the Allot function was included just to give you an idea of how the locks are made, as for pauseCB that should be re-mapped to a variant that calls something like pthread_yield(), it's what I tested with, anyways the general idea is that the shared lock defaults to NULL when not locked, when you want to lock it you 1st wait for it to be NULL then as soon as it's empty you fill it with your own pointer then wait for other threads who detected the same situation to fill there pointers into the shared pointer (assuming any other threads were trying to lock the pointer), when the wait is over you check again if the pointer is not matching your own, if not then you failed to lock and should wait longer, using pointers lends itself well to threading as the pointer being used to take the shared pointer will normally be unique to the thread (because rarely will anyone try to use the same pointer in multiple threads for this process that has a clear example in it's declaration), using const char * for the LOCK typedef means a thread can give more information about itself or the pointer it locked with if it so chooses. GRIP on the other hand is meant for multi-threaded data, for example assigning parts of a buffer, the owning thread should fill in the '_' member after acquiring the grip, if the previous grip doesn't have info about the section of the buffer it took then it should wait until it does, when it does it is then free to take it's own chunk after analysing what has already been taken by other grips linked to the shared grip, this method would lend itself well to graphics generation for example as a thread for each column could be launched in ascending order (so that the row contents are process from left to right), at least that's the theory on the grip one, requires testing which I'm sure you can do better than I can at the moment.
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.