2121// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2222// THE SOFTWARE.
2323
24+ #include < semaphore.h>
25+
2426#import < XCTest/XCTest.h>
2527#import " MSIDKeychainUtil.h"
2628#import " MSIDWorkPlaceJoinUtil.h"
@@ -70,6 +72,8 @@ - (void)setUp
7072#if TARGET_OS_OSX
7173 self.useIosStyleKeychain = NO ;
7274#endif
75+ [self .class initialize ];
76+ [self .class acquireGlobalWPJTestLockWithTimeout: 2.0 ];
7377}
7478
7579- (void )tearDown
@@ -79,6 +83,7 @@ - (void)tearDown
7983 [self cleanWPJ: [self keychainGroup: YES ]];
8084 [self cleanWPJ: [self keychainGroup: NO ]];
8185 }
86+ [self .class releaseGlobalWPJTestLock ];
8287 [MSIDTestSwizzle reset ];
8388}
8489
@@ -893,5 +898,64 @@ - (void)insertEccStkKeyForTenantIdentifier:(NSString *)tenantIdentifier
893898 privateKeyTag: stkTag
894899 accessGroup: keychainGroup];
895900}
901+ # pragma mark -- Serial execution of tests
902+
903+ // Global interprocess semaphore (named; no file fallback)
904+ static sem_t *s_wpjNamedSem = SEM_FAILED;
905+
906+ + (void )initialize
907+ {
908+ if (self != [MSIDWorkPlaceJoinUtilTests class ]) return ;
909+
910+ static dispatch_once_t onceToken;
911+ dispatch_once (&onceToken, ^{
912+ // Named POSIX semaphore (not file-based). Name must start with '/'.
913+ s_wpjNamedSem = sem_open (" /com.microsoft.msid.wpj.tests.sem" , O_CREAT, 0666 , 1 );
914+ });
915+ }
916+
917+ + (BOOL )acquireGlobalWPJTestLockWithTimeout : (NSTimeInterval )timeoutSec
918+ {
919+ if (s_wpjNamedSem == SEM_FAILED) return NO ;
920+
921+ // Blocking acquire if timeout is negative
922+ if (timeoutSec < 0 ) {
923+ int rc;
924+ do { rc = sem_wait (s_wpjNamedSem); } while (rc == -1 && errno == EINTR);
925+ return rc == 0 ;
926+ }
927+
928+ // Immediate try if timeout is zero
929+ if (timeoutSec == 0 ) {
930+ int rc;
931+ do { rc = sem_trywait (s_wpjNamedSem); } while (rc == -1 && errno == EINTR);
932+ return rc == 0 ;
933+ }
934+
935+ CFAbsoluteTime deadline = CFAbsoluteTimeGetCurrent () + timeoutSec;
936+ struct timespec sleepTs = { .tv_sec = 0 , .tv_nsec = 10 * 1000 * 1000 }; // 10ms
937+
938+ for (;;) {
939+ int rc = sem_trywait (s_wpjNamedSem);
940+ if (rc == 0 ) return YES ;
941+
942+ if (errno != EAGAIN && errno != EINTR) {
943+ return NO ; // unexpected error
944+ }
945+
946+ if (CFAbsoluteTimeGetCurrent () >= deadline) {
947+ return NO ; // timed out
948+ }
949+
950+ nanosleep (&sleepTs, NULL );
951+ }
952+ }
953+
954+ + (void )releaseGlobalWPJTestLock
955+ {
956+ if (s_wpjNamedSem != SEM_FAILED) {
957+ sem_post (s_wpjNamedSem);
958+ }
959+ }
896960
897961@end
0 commit comments