//============================================================ // // sdlsync.c - SDL core synchronization functions // // Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team. // Visit http://mamedev.org for licensing and usage restrictions. // // SDLMAME by Olivier Galibert and R. Belmont // //============================================================ #ifndef _GNU_SOURCE #define _GNU_SOURCE // for PTHREAD_MUTEX_RECURSIVE; needs to be here before other glibc headers are included #endif #include "sdlinc.h" // standard C headers #include #include // MAME headers #include "osdcore.h" #include "osinline.h" #include "sdlsync.h" #include "eminline.h" #define INCL_DOS #include #include #define pthread_t int #define pthread_self _gettid struct osd_lock { volatile pthread_t holder; INT32 count; #ifdef PTR64 INT8 padding[52]; // Fill a 64-byte cache line #else INT8 padding[56]; // A bit more padding #endif }; struct osd_event { HMTX hmtx; HEV hev; volatile INT32 autoreset; INT8 padding[52]; // Fill a 64-byte cache line }; //============================================================ // TYPE DEFINITIONS //============================================================ struct osd_thread { pthread_t thread; osd_thread_callback callback; void *param; }; struct osd_scalable_lock { struct { volatile INT32 haslock; // do we have the lock? INT32 filler[64/4-1]; // assumes a 64-byte cache line } slot[WORK_MAX_THREADS]; // one slot per thread volatile INT32 nextindex; // index of next slot to use }; //============================================================ // Scalable Locks //============================================================ osd_scalable_lock *osd_scalable_lock_alloc(void) { osd_scalable_lock *lock; lock = (osd_scalable_lock *)calloc(1, sizeof(*lock)); memset(lock, 0, sizeof(*lock)); lock->slot[0].haslock = TRUE; return lock; } INT32 osd_scalable_lock_acquire(osd_scalable_lock *lock) { INT32 myslot = (atomic_increment32(&lock->nextindex) - 1) & (WORK_MAX_THREADS - 1); #if defined(__i386__) || defined(__x86_64__) register INT32 tmp; __asm__ __volatile__ ( "1: clr %[tmp] ;" " xchg %[haslock], %[tmp] ;" " test %[tmp], %[tmp] ;" " jne 3f ;" "2: mov %[haslock], %[tmp] ;" " test %[tmp], %[tmp] ;" " jne 1b ;" " pause ;" " jmp 2b ;" "3: " : [haslock] "+m" (lock->slot[myslot].haslock) , [tmp] "=&r" (tmp) : : "%cc" ); #elif defined(__ppc__) || defined (__PPC__) || defined(__ppc64__) || defined(__PPC64__) register INT32 tmp; __asm__ __volatile__ ( "1: lwarx %[tmp], 0, %[haslock] \n" " cmpwi %[tmp], 0 \n" " bne 3f \n" "2: lwzx %[tmp], 0, %[haslock] \n" " cmpwi %[tmp], 0 \n" " bne 1b \n" " nop \n" " nop \n" " b 2b \n" "3: li %[tmp], 0 \n" " sync \n" " stwcx. %[tmp], 0, %[haslock] \n" " bne- 1b \n" " eieio \n" : [tmp] "=&r" (tmp) : [haslock] "r" (&lock->slot[myslot].haslock) : "cr0" ); #else INT32 backoff = 1; while (!osd_compare_exchange32(&lock->slot[myslot].haslock, TRUE, FALSE)) { INT32 backcount; for (backcount = 0; backcount < backoff; backcount++) osd_yield_processor(); backoff <<= 1; } #endif return myslot; } void osd_scalable_lock_release(osd_scalable_lock *lock, INT32 myslot) { #if defined(__i386__) || defined(__x86_64__) register INT32 tmp = TRUE; __asm__ __volatile__ ( " xchg %[haslock], %[tmp] ;" : [haslock] "+m" (lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock) , [tmp] "+r" (tmp) : ); #elif defined(__ppc__) || defined (__PPC__) || defined(__ppc64__) || defined(__PPC64__) lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock = TRUE; __asm__ __volatile__ ( " eieio " : : ); #else osd_exchange32(&lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock, TRUE); #endif } void osd_scalable_lock_free(osd_scalable_lock *lock) { free(lock); } INLINE pthread_t osd_compare_exchange_pthread_t(pthread_t volatile *ptr, pthread_t compare, pthread_t exchange) { #ifdef PTR64 INT64 result = compare_exchange64((INT64 volatile *)ptr, (INT64)compare, (INT64)exchange); #else INT32 result = compare_exchange32((INT32 volatile *)ptr, (INT32)compare, (INT32)exchange); #endif return (pthread_t)result; } INLINE pthread_t osd_exchange_pthread_t(pthread_t volatile *ptr, pthread_t exchange) { #ifdef PTR64 INT64 result = osd_exchange64((INT64 volatile *)ptr, (INT64)exchange); #else INT32 result = atomic_exchange32((INT32 volatile *)ptr, (INT32)exchange); #endif return (pthread_t)result; } //============================================================ // osd_lock_alloc //============================================================ osd_lock *osd_lock_alloc(void) { osd_lock *lock; lock = (osd_lock *)calloc(1, sizeof(osd_lock)); lock->holder = 0; lock->count = 0; return lock; } //============================================================ // osd_lock_acquire //============================================================ void osd_lock_acquire(osd_lock *lock) { pthread_t current, prev; current = pthread_self(); prev = osd_compare_exchange_pthread_t(&lock->holder, 0, current); if (prev != (size_t)NULL && prev != current) { do { register INT32 spin = 10000; // Convenient spin count register pthread_t tmp; #if defined(__i386__) || defined(__x86_64__) __asm__ __volatile__ ( "1: pause ;" " mov %[holder], %[tmp] ;" " test %[tmp], %[tmp] ;" " loopne 1b ;" : [spin] "+c" (spin) , [tmp] "=&r" (tmp) : [holder] "m" (lock->holder) : "%cc" ); #elif defined(__ppc__) || defined(__PPC__) __asm__ __volatile__ ( "1: nop \n" " nop \n" " lwzx %[tmp], 0, %[holder] \n" " cmpwi %[tmp], 0 \n" " bdnzt eq, 1b \n" : [spin] "+c" (spin) , [tmp] "=&r" (tmp) : [holder] "r" (&lock->holder) : "cr0" ); #elif defined(__ppc64__) || defined(__PPC64__) __asm__ __volatile__ ( "1: nop \n" " nop \n" " ldx %[tmp], 0, %[holder] \n" " cmpdi %[tmp], 0 \n" " bdnzt eq, 1b \n" : [spin] "+c" (spin) , [tmp] "=&r" (tmp) : [holder] "r" (&lock->holder) : "cr0" ); #else while (--spin > 0 && lock->holder != NULL) osd_yield_processor(); #endif #if 0 /* If you mean to use locks as a blocking mechanism for extended * periods of time, you should do something like this. However, * it kills the performance of gaelco3d. */ if (spin == 0) { struct timespec sleep = { 0, 100000 }, remaining; nanosleep(&sleep, &remaining); // sleep for 100us } #endif } while (osd_compare_exchange_pthread_t(&lock->holder, 0, current) != (size_t)NULL); } lock->count++; } //============================================================ // osd_lock_try //============================================================ int osd_lock_try(osd_lock *lock) { pthread_t current, prev; current = pthread_self(); prev = osd_compare_exchange_pthread_t(&lock->holder, 0, current); if (prev == (size_t)NULL || prev == current) { lock->count++; return 1; } return 0; } //============================================================ // osd_lock_release //============================================================ void osd_lock_release(osd_lock *lock) { pthread_t current; current = pthread_self(); if (lock->holder == current) { if (--lock->count == 0) #if defined(__ppc__) || defined(__PPC__) || defined(__ppc64__) || defined(__PPC64__) lock->holder = 0; __asm__ __volatile__( " eieio " : : ); #else osd_exchange_pthread_t(&lock->holder, 0); #endif return; } // trying to release a lock you don't hold is bad! // assert(lock->holder == pthread_self()); } //============================================================ // osd_lock_free //============================================================ void osd_lock_free(osd_lock *lock) { free(lock); } //============================================================ // osd_event_alloc //============================================================ osd_event *osd_event_alloc(int manualreset, int initialstate) { osd_event *ev; ev = (osd_event *)calloc(1, sizeof(osd_event)); DosCreateMutexSem(NULL, &ev->hmtx, 0, FALSE); DosCreateEventSem(NULL, &ev->hev, 0, initialstate); ev->autoreset = !manualreset; return ev; } //============================================================ // osd_event_free //============================================================ void osd_event_free(osd_event *event) { DosCloseMutexSem(event->hmtx); DosCloseEventSem(event->hev); free(event); } //============================================================ // osd_event_set //============================================================ void osd_event_set(osd_event *event) { DosPostEventSem(event->hev); } //============================================================ // osd_event_reset //============================================================ void osd_event_reset(osd_event *event) { ULONG ulCount; DosResetEventSem(event->hev, &ulCount); } //============================================================ // osd_event_wait //============================================================ int osd_event_wait(osd_event *event, osd_ticks_t timeout) { ULONG rc; if(event->autoreset) DosRequestMutexSem(event->hmtx, -1); rc = DosWaitEventSem(event->hev, timeout * 1000 / osd_ticks_per_second()); if(event->autoreset) { ULONG ulCount; if(rc == 0) DosResetEventSem(event->hev, &ulCount); DosReleaseMutexSem(event->hmtx); } return (rc == 0); } //============================================================ // osd_thread_create //============================================================ static void worker_thread_entry(void *param) { osd_thread *thread = (osd_thread *) param; thread->callback(thread->param); } osd_thread *osd_thread_create(osd_thread_callback callback, void *cbparam) { osd_thread *thread; thread = (osd_thread *)calloc(1, sizeof(osd_thread)); thread->callback = callback; thread->param = cbparam; thread->thread = _beginthread(worker_thread_entry, NULL, 65535, thread); if ( thread->thread == -1 ) { free(thread); return NULL; } return thread; } //============================================================ // osd_thread_adjust_priority //============================================================ int osd_thread_adjust_priority(osd_thread *thread, int adjust) { PTIB ptib; DosGetInfoBlocks(&ptib, NULL); if ( DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, ((BYTE)ptib->tib_ptib2->tib2_ulpri) + adjust, thread->thread )) return FALSE; return TRUE; } //============================================================ // osd_thread_cpu_affinity //============================================================ int osd_thread_cpu_affinity(osd_thread *thread, UINT32 mask) { return TRUE; } //============================================================ // osd_thread_wait_free //============================================================ void osd_thread_wait_free(osd_thread *thread) { TID tid = thread->thread; DosWaitThread(&tid, 0); free(thread); } //============================================================ // osd_process_kill //============================================================ void osd_process_kill(void) { PPIB ppib; DosGetInfoBlocks(NULL, &ppib); DosKillProcess(DKP_PROCESSTREE, ppib->pib_ulpid); }