From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-3.1 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 12883 invoked from network); 21 Feb 2022 11:40:43 -0000 Received: from mother.openwall.net (195.42.179.200) by inbox.vuxu.org with ESMTPUTF8; 21 Feb 2022 11:40:43 -0000 Received: (qmail 23666 invoked by uid 550); 21 Feb 2022 11:40:39 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Reply-To: musl@lists.openwall.com Received: (qmail 23632 invoked from network); 21 Feb 2022 11:40:38 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:reply-to:from:date:message-id:subject:to; bh=K0KwneJqrv2OhB9c/Qlmqx2yGdnN9w2m4rLuEOrznQg=; b=HPfx7U2SPKd4piVgJ3TKmi154FHmzqATHulcgqJMtIuRNVa2NwF1WWj3w3n9/+bO4E PmmzcxCIQIhNMhKt9UVHfY+rpkfiCh9Tw/fjkPlnitibtZkkuaEGtxHqbisjBb8cBm8S owzaWb+j3RHumBPDq4FkDmi7iPz74IaQdVLlha7Z4YDDRqN//nYrTmNHLBapm8Llwv5v dsaK37aD3VYurE27iehcIdu3mDRNZe6uRS1IPtnniqX6Kgw9cbD98MMZ95yquEfoScEK Z/ZQH6eZEdJTW42l/mZqNYIp8nJ5++taO5EwHV/w7E4qF9zf0EigfYUh/ArJiQE5fGXl I/gg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:reply-to:from:date:message-id :subject:to; bh=K0KwneJqrv2OhB9c/Qlmqx2yGdnN9w2m4rLuEOrznQg=; b=zcP7KV8/DR38k0AHBrX1hg/NR6IpYunYNo0zSH4KM5o7804gGmWsukMwluAOFcGHSP iDouLlewk3d9WhC5JEHQz1p8JWB9ZY504k8AgCfrvkgRLemLaDeNowQgNOXOLIF9TXzt i5dy4WuIW2ik3yNknTWHkVqqiMQE+eFzN3CMpBDDnBHYrlw1FL3aON6YBSAFryhc9qSN Bfcfg0EkPMJUtB5/ZoZT5a7IVY02WOtRP4X/TSsR+zOxrsEdMUGLhh8I9rQgmbpJCTQk +FUEFctXfvqAUbIOig3wfdCzCgq1AjptYKr+FY0xa8lCW2GYuTkyTPHf5zPIssiEeP0v lG7g== X-Gm-Message-State: AOAM532l5cwRtHauI9oxxqbSPwOuZ9KyISpOvXjFK/oaZ7+Le4CjVpQe FCTiZE8AuZ5JRZv9oqby6548rhLkVDo0JVvOP1MwezwLgTg= X-Google-Smtp-Source: ABdhPJxqOI6HXzF6+h6t1gEgc8DT9cTlcGP1zMTYSuEftXpWdpKRBkVZhM30uZiuqDTdlFlBzPTvd1Y9UUuNwn2oUIw= X-Received: by 2002:a67:fc13:0:b0:31c:5602:12f with SMTP id o19-20020a67fc13000000b0031c5602012fmr1410751vsq.38.1645443626240; Mon, 21 Feb 2022 03:40:26 -0800 (PST) MIME-Version: 1.0 From: Lee Shallis Date: Mon, 21 Feb 2022 11:36:06 +0000 Message-ID: To: musl@lists.openwall.com Content-Type: text/plain; charset="UTF-8" Subject: [musl] 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.