mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
Merged winwork and sdlwork into osd/modules/sync/work_osd.c. Moved
miniwork.c to osd/modules/sync/work_mini.c The makefile now adds either a OSD_SDL, OSD_WINDOWS or OSD_MINI define to DEFS. This was necessary so that work_osd.c could determine the build on a OSD level. (nw)
This commit is contained in:
parent
8fefbb2969
commit
2c4b785554
15
makefile
15
makefile
@ -491,7 +491,22 @@ ifdef FASTDEBUG
|
||||
DEFS += -DMAME_DEBUG_FAST
|
||||
endif
|
||||
|
||||
# add a define identifying the target osd
|
||||
|
||||
ifeq ($(OSD),sdl)
|
||||
DEFS += -DOSD_SDL
|
||||
else
|
||||
ifeq ($(OSD),windows)
|
||||
DEFS += -DOSD_WINDOWS
|
||||
else
|
||||
ifeq ($(OSD),osdmini)
|
||||
DEFS += -DOSD_MINI
|
||||
else
|
||||
$(error Unknown OSD)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#-------------------------------------------------
|
||||
# compile flags
|
||||
# CCOMFLAGS are common flags
|
||||
|
@ -2,10 +2,14 @@
|
||||
// copyright-holders:Aaron Giles
|
||||
//============================================================
|
||||
//
|
||||
// winwork.c - Win32 OSD core work item functions
|
||||
// sdlwork.c - SDL OSD core work item functions
|
||||
//
|
||||
// Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
|
||||
// Visit http://mamedev.org for licensing and usage restrictions.
|
||||
//
|
||||
//============================================================
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
// standard windows headers
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
@ -16,15 +20,26 @@
|
||||
#ifdef __GNUC__
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// MAME headers
|
||||
#include "osdcore.h"
|
||||
|
||||
#include "modules/sync/osdsync.h"
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
#include "winos.h"
|
||||
#elif defined(OSD_SDL)
|
||||
#include "sdlos.h"
|
||||
typedef void *PVOID;
|
||||
#endif
|
||||
|
||||
#include "eminline.h"
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
#include "osxutils.h"
|
||||
#endif
|
||||
|
||||
|
||||
//============================================================
|
||||
// DEBUGGING
|
||||
@ -37,11 +52,13 @@
|
||||
//============================================================
|
||||
|
||||
#define ENV_PROCESSORS "OSDPROCESSORS"
|
||||
#define ENV_WORKQUEUEMAXTHREADS "OSDWORKQUEUEMAXTHREADS"
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
#define SPIN_LOOP_TIME (osd_ticks_per_second() / 1000)
|
||||
|
||||
|
||||
#else
|
||||
#define INFINITE (osd_ticks_per_second() * (osd_ticks_t) 10000)
|
||||
#define SPIN_LOOP_TIME (osd_ticks_per_second() / 10000)
|
||||
#endif
|
||||
|
||||
//============================================================
|
||||
// MACROS
|
||||
@ -187,18 +204,25 @@ osd_work_queue *osd_work_queue_alloc(int flags)
|
||||
// on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
|
||||
if (numprocs == 1)
|
||||
queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
// on an n-CPU system, create n threads for multi queues, and 1 thread for everything else
|
||||
else
|
||||
queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? numprocs : 1;
|
||||
#else
|
||||
// on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
|
||||
else
|
||||
queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
|
||||
#endif
|
||||
|
||||
if (osdworkqueuemaxthreads != NULL && sscanf(osdworkqueuemaxthreads, "%d", &threadnum) == 1 && queue->threads > threadnum)
|
||||
queue->threads = threadnum;
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
// multi-queues with high frequency items should top out at 4 for now
|
||||
// since we have scaling problems above that
|
||||
if ((flags & WORK_QUEUE_FLAG_HIGH_FREQ) && queue->threads > 1)
|
||||
queue->threads = MIN(queue->threads - 1, 4);
|
||||
#endif
|
||||
|
||||
// clamp to the maximum
|
||||
queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
|
||||
@ -287,8 +311,18 @@ int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
|
||||
|
||||
// spin until we're done
|
||||
begin_timing(thread->spintime);
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
while (queue->items != 0 && osd_ticks() < stopspin)
|
||||
osd_yield_processor();
|
||||
#else
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && queue->items != 0)
|
||||
osd_yield_processor();
|
||||
} while (queue->items != 0 && osd_ticks() < stopspin);
|
||||
#endif
|
||||
|
||||
end_timing(thread->spintime);
|
||||
|
||||
begin_timing(thread->waittime);
|
||||
@ -495,8 +529,11 @@ osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_call
|
||||
|
||||
// if no threads, run the queue now on this thread
|
||||
if (queue->threads == 0)
|
||||
{
|
||||
end_timing(queue->thread[0].waittime);
|
||||
worker_thread_process(queue, &queue->thread[0]);
|
||||
|
||||
begin_timing(queue->thread[0].waittime);
|
||||
}
|
||||
// only return the item if it won't get released automatically
|
||||
return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
|
||||
}
|
||||
@ -526,8 +563,16 @@ int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
|
||||
if (item->event == NULL)
|
||||
{
|
||||
osd_ticks_t stopspin = osd_ticks() + timeout;
|
||||
#if defined(OSD_WINDOWS)
|
||||
while (!item->done && osd_ticks() < stopspin)
|
||||
osd_yield_processor();
|
||||
#else
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && !item->done)
|
||||
osd_yield_processor();
|
||||
} while (!item->done && osd_ticks() < stopspin);
|
||||
#endif
|
||||
}
|
||||
|
||||
// otherwise, block on the event until done
|
||||
@ -610,6 +655,10 @@ static void *worker_thread_entry(void *param)
|
||||
work_thread_info *thread = (work_thread_info *)param;
|
||||
osd_work_queue *queue = thread->queue;
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
void *arp = NewAutoreleasePool();
|
||||
#endif
|
||||
|
||||
// loop until we exit
|
||||
for ( ;; )
|
||||
{
|
||||
@ -646,8 +695,18 @@ static void *worker_thread_entry(void *param)
|
||||
// spin for a while looking for more work
|
||||
begin_timing(thread->spintime);
|
||||
stopspin = osd_ticks() + SPIN_LOOP_TIME;
|
||||
|
||||
#if defined(OSD_WINDOWS)
|
||||
while (queue->list == NULL && osd_ticks() < stopspin)
|
||||
osd_yield_processor();
|
||||
#else
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && queue->list == NULL)
|
||||
osd_yield_processor();
|
||||
} while (queue->list == NULL && osd_ticks() < stopspin);
|
||||
#endif
|
||||
|
||||
end_timing(thread->spintime);
|
||||
}
|
||||
|
||||
@ -661,6 +720,11 @@ static void *worker_thread_entry(void *param)
|
||||
atomic_exchange32(&thread->active, FALSE);
|
||||
atomic_decrement32(&queue->livethreads);
|
||||
}
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
ReleaseAutoreleasePool(arp);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -45,7 +45,10 @@
|
||||
MINISRC = $(SRC)/osd/$(OSD)
|
||||
MINIOBJ = $(OBJ)/osd/$(OSD)
|
||||
|
||||
OBJDIRS += $(MINIOBJ)
|
||||
OSDSRC = $(SRC)/osd
|
||||
OSDOBJ = $(OBJ)/osd
|
||||
|
||||
OBJDIRS += $(MINIOBJ) $(OSDOBJ)/modules/sync
|
||||
|
||||
|
||||
|
||||
@ -59,9 +62,7 @@ OSDCOREOBJS = \
|
||||
$(MINIOBJ)/minimisc.o \
|
||||
$(MINIOBJ)/minisync.o \
|
||||
$(MINIOBJ)/minitime.o \
|
||||
$(MINIOBJ)/miniwork.o \
|
||||
|
||||
|
||||
$(OSDOBJ)/modules/sync/work_mini.o \
|
||||
|
||||
#-------------------------------------------------
|
||||
# OSD mini library
|
||||
|
@ -405,8 +405,13 @@ OSDCOREOBJS = \
|
||||
$(SDLOBJ)/sdlsocket.o \
|
||||
$(SDLOBJ)/sdlmisc_$(BASE_TARGETOS).o \
|
||||
$(SDLOBJ)/sdlos_$(SDLOS_TARGETOS).o \
|
||||
$(OSDOBJ)/modules/sync/sync_$(SYNC_IMPLEMENTATION).o \
|
||||
$(SDLOBJ)/sdlwork.o
|
||||
$(OSDOBJ)/modules/sync/sync_$(SYNC_IMPLEMENTATION).o
|
||||
|
||||
ifdef NOASM
|
||||
OSDCOREOBJS += $(OSDOBJ)/modules/sync/work_mini.o
|
||||
else
|
||||
OSDCOREOBJS += $(OSDOBJ)/modules/sync/work_osd.o
|
||||
endif
|
||||
|
||||
# any "main" must be in LIBOSD or else the build will fail!
|
||||
# for the windows build, we just add it to libocore as well.
|
||||
|
@ -1,761 +0,0 @@
|
||||
//============================================================
|
||||
//
|
||||
// sdlwork.c - SDL OSD core work item 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
|
||||
//
|
||||
//============================================================
|
||||
|
||||
#if defined(SDLMAME_NOASM)
|
||||
|
||||
#include "../osdmini/miniwork.c"
|
||||
|
||||
#else
|
||||
|
||||
#include "osdcore.h"
|
||||
#include "osinline.h"
|
||||
|
||||
#include "modules/sync/osdsync.h"
|
||||
#include "sdlos.h"
|
||||
|
||||
#include "eminline.h"
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
#include "osxutils.h"
|
||||
#endif
|
||||
|
||||
//============================================================
|
||||
// DEBUGGING
|
||||
//============================================================
|
||||
|
||||
#define KEEP_STATISTICS (0)
|
||||
|
||||
//============================================================
|
||||
// PARAMETERS
|
||||
//============================================================
|
||||
|
||||
#define ENV_PROCESSORS "OSDPROCESSORS"
|
||||
#define ENV_WORKQUEUEMAXTHREADS "OSDWORKQUEUEMAXTHREADS"
|
||||
|
||||
#define INFINITE (osd_ticks_per_second() * (osd_ticks_t) 10000)
|
||||
#define SPIN_LOOP_TIME (osd_ticks_per_second() / 10000)
|
||||
|
||||
|
||||
//============================================================
|
||||
// MACROS
|
||||
//============================================================
|
||||
|
||||
#if KEEP_STATISTICS
|
||||
#define add_to_stat(v,x) do { atomic_add32((v), (x)); } while (0)
|
||||
#define begin_timing(v) do { (v) -= get_profile_ticks(); } while (0)
|
||||
#define end_timing(v) do { (v) += get_profile_ticks(); } while (0)
|
||||
#else
|
||||
#define add_to_stat(v,x) do { } while (0)
|
||||
#define begin_timing(v) do { } while (0)
|
||||
#define end_timing(v) do { } while (0)
|
||||
#endif
|
||||
|
||||
//============================================================
|
||||
// TYPE DEFINITIONS
|
||||
//============================================================
|
||||
|
||||
struct work_thread_info
|
||||
{
|
||||
osd_work_queue * queue; // pointer back to the queue
|
||||
osd_thread * handle; // handle to the thread
|
||||
osd_event * wakeevent; // wake event for the thread
|
||||
volatile INT32 active; // are we actively processing work?
|
||||
|
||||
#if KEEP_STATISTICS
|
||||
INT32 itemsdone;
|
||||
osd_ticks_t actruntime;
|
||||
osd_ticks_t runtime;
|
||||
osd_ticks_t spintime;
|
||||
osd_ticks_t waittime;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct osd_work_queue
|
||||
{
|
||||
osd_scalable_lock * lock; // lock for protecting the queue
|
||||
osd_work_item * volatile list; // list of items in the queue
|
||||
osd_work_item ** volatile tailptr; // pointer to the tail pointer of work items in the queue
|
||||
osd_work_item * volatile free; // free list of work items
|
||||
volatile INT32 items; // items in the queue
|
||||
volatile INT32 livethreads; // number of live threads
|
||||
volatile INT32 waiting; // is someone waiting on the queue to complete?
|
||||
volatile INT32 exiting; // should the threads exit on their next opportunity?
|
||||
UINT32 threads; // number of threads in this queue
|
||||
UINT32 flags; // creation flags
|
||||
work_thread_info * thread; // array of thread information
|
||||
osd_event * doneevent; // event signalled when work is complete
|
||||
|
||||
#if KEEP_STATISTICS
|
||||
volatile INT32 itemsqueued; // total items queued
|
||||
volatile INT32 setevents; // number of times we called SetEvent
|
||||
volatile INT32 extraitems; // how many extra items we got after the first in the queue loop
|
||||
volatile INT32 spinloops; // how many times spinning bought us more items
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct osd_work_item
|
||||
{
|
||||
osd_work_item * next; // pointer to next item
|
||||
osd_work_queue * queue; // pointer back to the owning queue
|
||||
osd_work_callback callback; // callback function
|
||||
void * param; // callback parameter
|
||||
void * result; // callback result
|
||||
osd_event * event; // event signalled when complete
|
||||
UINT32 flags; // creation flags
|
||||
volatile INT32 done; // is the item done?
|
||||
};
|
||||
|
||||
typedef void *PVOID;
|
||||
|
||||
//============================================================
|
||||
// GLOBAL VARIABLES
|
||||
//============================================================
|
||||
|
||||
int osd_num_processors = 0;
|
||||
|
||||
//============================================================
|
||||
// FUNCTION PROTOTYPES
|
||||
//============================================================
|
||||
|
||||
static int effective_num_processors(void);
|
||||
static void * worker_thread_entry(void *param);
|
||||
static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
|
||||
static bool queue_has_list_items(osd_work_queue *queue);
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_queue_alloc
|
||||
//============================================================
|
||||
|
||||
osd_work_queue *osd_work_queue_alloc(int flags)
|
||||
{
|
||||
int numprocs = effective_num_processors();
|
||||
osd_work_queue *queue;
|
||||
int threadnum;
|
||||
char *osdworkqueuemaxthreads = osd_getenv("OSDWORKQUEUEMAXTHREADS");
|
||||
|
||||
// allocate a new queue
|
||||
queue = (osd_work_queue *)osd_malloc(sizeof(*queue));
|
||||
if (queue == NULL)
|
||||
goto error;
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
|
||||
// initialize basic queue members
|
||||
queue->tailptr = (osd_work_item **)&queue->list;
|
||||
queue->flags = flags;
|
||||
|
||||
// allocate events for the queue
|
||||
queue->doneevent = osd_event_alloc(TRUE, TRUE); // manual reset, signalled
|
||||
if (queue->doneevent == NULL)
|
||||
goto error;
|
||||
|
||||
// initialize the critical section
|
||||
queue->lock = osd_scalable_lock_alloc();
|
||||
if (queue->lock == NULL)
|
||||
goto error;
|
||||
|
||||
// determine how many threads to create...
|
||||
// on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
|
||||
if (numprocs == 1)
|
||||
queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
|
||||
|
||||
// on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
|
||||
else
|
||||
queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
|
||||
|
||||
if (osdworkqueuemaxthreads != NULL && sscanf(osdworkqueuemaxthreads, "%d", &threadnum) == 1 && queue->threads > threadnum)
|
||||
queue->threads = threadnum;
|
||||
|
||||
|
||||
// clamp to the maximum
|
||||
queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
|
||||
|
||||
// allocate memory for thread array (+1 to count the calling thread)
|
||||
queue->thread = (work_thread_info *)osd_malloc_array((queue->threads + 1) * sizeof(queue->thread[0]));
|
||||
if (queue->thread == NULL)
|
||||
goto error;
|
||||
memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
|
||||
|
||||
// iterate over threads
|
||||
for (threadnum = 0; threadnum < queue->threads; threadnum++)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[threadnum];
|
||||
|
||||
// set a pointer back to the queue
|
||||
thread->queue = queue;
|
||||
|
||||
// create the per-thread wake event
|
||||
thread->wakeevent = osd_event_alloc(FALSE, FALSE); // auto-reset, not signalled
|
||||
if (thread->wakeevent == NULL)
|
||||
goto error;
|
||||
|
||||
// create the thread
|
||||
thread->handle = osd_thread_create(worker_thread_entry, thread);
|
||||
if (thread->handle == NULL)
|
||||
goto error;
|
||||
|
||||
// set its priority: I/O threads get high priority because they are assumed to be
|
||||
// blocked most of the time; other threads just match the creator's priority
|
||||
if (flags & WORK_QUEUE_FLAG_IO)
|
||||
osd_thread_adjust_priority(thread->handle, 1);
|
||||
else
|
||||
osd_thread_adjust_priority(thread->handle, 0);
|
||||
}
|
||||
|
||||
// start a timer going for "waittime" on the main thread
|
||||
begin_timing(queue->thread[queue->threads].waittime);
|
||||
return queue;
|
||||
|
||||
error:
|
||||
osd_work_queue_free(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_queue_items
|
||||
//============================================================
|
||||
|
||||
int osd_work_queue_items(osd_work_queue *queue)
|
||||
{
|
||||
// return the number of items currently in the queue
|
||||
return queue->items;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_queue_wait
|
||||
//============================================================
|
||||
|
||||
int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
|
||||
{
|
||||
// if no threads, no waiting
|
||||
if (queue->threads == 0)
|
||||
return TRUE;
|
||||
|
||||
// if no items, we're done
|
||||
if (queue->items == 0)
|
||||
return TRUE;
|
||||
|
||||
// if this is a multi queue, help out rather than doing nothing
|
||||
if (queue->flags & WORK_QUEUE_FLAG_MULTI)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[queue->threads];
|
||||
|
||||
end_timing(thread->waittime);
|
||||
|
||||
// process what we can as a worker thread
|
||||
worker_thread_process(queue, thread);
|
||||
|
||||
// if we're a high frequency queue, spin until done
|
||||
if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->items != 0)
|
||||
{
|
||||
osd_ticks_t stopspin = osd_ticks() + timeout;
|
||||
|
||||
// spin until we're done
|
||||
begin_timing(thread->spintime);
|
||||
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && queue->items != 0)
|
||||
osd_yield_processor();
|
||||
} while (queue->items != 0 && osd_ticks() < stopspin);
|
||||
end_timing(thread->spintime);
|
||||
|
||||
begin_timing(thread->waittime);
|
||||
return (queue->items == 0);
|
||||
}
|
||||
begin_timing(thread->waittime);
|
||||
}
|
||||
|
||||
// reset our done event and double-check the items before waiting
|
||||
osd_event_reset(queue->doneevent);
|
||||
atomic_exchange32(&queue->waiting, TRUE);
|
||||
if (queue->items != 0)
|
||||
osd_event_wait(queue->doneevent, timeout);
|
||||
atomic_exchange32(&queue->waiting, FALSE);
|
||||
|
||||
// return TRUE if we actually hit 0
|
||||
return (queue->items == 0);
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_queue_free
|
||||
//============================================================
|
||||
|
||||
void osd_work_queue_free(osd_work_queue *queue)
|
||||
{
|
||||
// if we have threads, clean them up
|
||||
if (queue->thread != NULL)
|
||||
{
|
||||
int threadnum;
|
||||
|
||||
// stop the timer for "waittime" on the main thread
|
||||
end_timing(queue->thread[queue->threads].waittime);
|
||||
|
||||
// signal all the threads to exit
|
||||
atomic_exchange32(&queue->exiting, TRUE);
|
||||
for (threadnum = 0; threadnum < queue->threads; threadnum++)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[threadnum];
|
||||
if (thread->wakeevent != NULL)
|
||||
osd_event_set(thread->wakeevent);
|
||||
}
|
||||
|
||||
// wait for all the threads to go away
|
||||
for (threadnum = 0; threadnum < queue->threads; threadnum++)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[threadnum];
|
||||
|
||||
// block on the thread going away, then close the handle
|
||||
if (thread->handle != NULL)
|
||||
{
|
||||
osd_thread_wait_free(thread->handle);
|
||||
}
|
||||
|
||||
// clean up the wake event
|
||||
if (thread->wakeevent != NULL)
|
||||
osd_event_free(thread->wakeevent);
|
||||
}
|
||||
|
||||
#if KEEP_STATISTICS
|
||||
// output per-thread statistics
|
||||
for (threadnum = 0; threadnum <= queue->threads; threadnum++)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[threadnum];
|
||||
osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
|
||||
printf("Thread %d: items=%9d run=%5.2f%% (%5.2f%%) spin=%5.2f%% wait/other=%5.2f%% total=%9d\n",
|
||||
threadnum, thread->itemsdone,
|
||||
(double)thread->runtime * 100.0 / (double)total,
|
||||
(double)thread->actruntime * 100.0 / (double)total,
|
||||
(double)thread->spintime * 100.0 / (double)total,
|
||||
(double)thread->waittime * 100.0 / (double)total,
|
||||
(UINT32) total);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// free the list
|
||||
if (queue->thread != NULL)
|
||||
osd_free(queue->thread);
|
||||
|
||||
// free all the events
|
||||
if (queue->doneevent != NULL)
|
||||
osd_event_free(queue->doneevent);
|
||||
|
||||
// free all items in the free list
|
||||
while (queue->free != NULL)
|
||||
{
|
||||
osd_work_item *item = (osd_work_item *)queue->free;
|
||||
queue->free = item->next;
|
||||
if (item->event != NULL)
|
||||
osd_event_free(item->event);
|
||||
osd_free(item);
|
||||
}
|
||||
|
||||
// free all items in the active list
|
||||
while (queue->list != NULL)
|
||||
{
|
||||
osd_work_item *item = (osd_work_item *)queue->list;
|
||||
queue->list = item->next;
|
||||
if (item->event != NULL)
|
||||
osd_event_free(item->event);
|
||||
osd_free(item);
|
||||
}
|
||||
|
||||
#if KEEP_STATISTICS
|
||||
printf("Items queued = %9d\n", queue->itemsqueued);
|
||||
printf("SetEvent calls = %9d\n", queue->setevents);
|
||||
printf("Extra items = %9d\n", queue->extraitems);
|
||||
printf("Spin loops = %9d\n", queue->spinloops);
|
||||
#endif
|
||||
|
||||
osd_scalable_lock_free(queue->lock);
|
||||
// free the queue itself
|
||||
osd_free(queue);
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_item_queue_multiple
|
||||
//============================================================
|
||||
|
||||
osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
|
||||
{
|
||||
osd_work_item *itemlist = NULL, *lastitem = NULL;
|
||||
osd_work_item **item_tailptr = &itemlist;
|
||||
INT32 lockslot;
|
||||
int itemnum;
|
||||
|
||||
// loop over items, building up a local list of work
|
||||
for (itemnum = 0; itemnum < numitems; itemnum++)
|
||||
{
|
||||
osd_work_item *item;
|
||||
|
||||
// first allocate a new work item; try the free list first
|
||||
INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
|
||||
do
|
||||
{
|
||||
item = (osd_work_item *)queue->free;
|
||||
} while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
|
||||
osd_scalable_lock_release(queue->lock, lockslot);
|
||||
|
||||
// if nothing, allocate something new
|
||||
if (item == NULL)
|
||||
{
|
||||
// allocate the item
|
||||
item = (osd_work_item *)osd_malloc(sizeof(*item));
|
||||
if (item == NULL)
|
||||
return NULL;
|
||||
item->event = NULL;
|
||||
item->queue = queue;
|
||||
item->done = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
atomic_exchange32(&item->done, FALSE); // needs to be set this way to prevent data race/usage of uninitialized memory on Linux
|
||||
}
|
||||
|
||||
// fill in the basics
|
||||
item->next = NULL;
|
||||
item->callback = callback;
|
||||
item->param = parambase;
|
||||
item->result = NULL;
|
||||
item->flags = flags;
|
||||
|
||||
// advance to the next
|
||||
lastitem = item;
|
||||
*item_tailptr = item;
|
||||
item_tailptr = &item->next;
|
||||
parambase = (UINT8 *)parambase + paramstep;
|
||||
}
|
||||
|
||||
// enqueue the whole thing within the critical section
|
||||
lockslot = osd_scalable_lock_acquire(queue->lock);
|
||||
*queue->tailptr = itemlist;
|
||||
queue->tailptr = item_tailptr;
|
||||
osd_scalable_lock_release(queue->lock, lockslot);
|
||||
|
||||
// increment the number of items in the queue
|
||||
atomic_add32(&queue->items, numitems);
|
||||
add_to_stat(&queue->itemsqueued, numitems);
|
||||
|
||||
// look for free threads to do the work
|
||||
if (queue->livethreads < queue->threads)
|
||||
{
|
||||
int threadnum;
|
||||
|
||||
// iterate over all the threads
|
||||
for (threadnum = 0; threadnum < queue->threads; threadnum++)
|
||||
{
|
||||
work_thread_info *thread = &queue->thread[threadnum];
|
||||
|
||||
// if this thread is not active, wake him up
|
||||
if (!thread->active)
|
||||
{
|
||||
osd_event_set(thread->wakeevent);
|
||||
add_to_stat(&queue->setevents, 1);
|
||||
|
||||
// for non-shared, the first one we find is good enough
|
||||
if (--numitems == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no threads, run the queue now on this thread
|
||||
if (queue->threads == 0)
|
||||
{
|
||||
end_timing(queue->thread[0].waittime);
|
||||
worker_thread_process(queue, &queue->thread[0]);
|
||||
begin_timing(queue->thread[0].waittime);
|
||||
}
|
||||
// only return the item if it won't get released automatically
|
||||
return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_item_wait
|
||||
//============================================================
|
||||
|
||||
int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
|
||||
{
|
||||
// if we're done already, just return
|
||||
if (item->done)
|
||||
return TRUE;
|
||||
|
||||
// if we don't have an event, create one
|
||||
if (item->event == NULL)
|
||||
{
|
||||
INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
|
||||
item->event = osd_event_alloc(TRUE, FALSE); // manual reset, not signalled
|
||||
osd_scalable_lock_release(item->queue->lock, lockslot);
|
||||
}
|
||||
else
|
||||
osd_event_reset(item->event);
|
||||
|
||||
// if we don't have an event, we need to spin (shouldn't ever really happen)
|
||||
if (item->event == NULL)
|
||||
{
|
||||
osd_ticks_t stopspin = osd_ticks() + timeout;
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && !item->done)
|
||||
osd_yield_processor();
|
||||
} while (!item->done && osd_ticks() < stopspin);
|
||||
}
|
||||
|
||||
// otherwise, block on the event until done
|
||||
else if (!item->done)
|
||||
osd_event_wait(item->event, timeout);
|
||||
|
||||
// return TRUE if the refcount actually hit 0
|
||||
return item->done;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_item_result
|
||||
//============================================================
|
||||
|
||||
void *osd_work_item_result(osd_work_item *item)
|
||||
{
|
||||
return item->result;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// osd_work_item_release
|
||||
//============================================================
|
||||
|
||||
void osd_work_item_release(osd_work_item *item)
|
||||
{
|
||||
osd_work_item *next;
|
||||
|
||||
// make sure we're done first
|
||||
osd_work_item_wait(item, 100 * osd_ticks_per_second());
|
||||
|
||||
// add us to the free list on our queue
|
||||
INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
|
||||
do
|
||||
{
|
||||
next = (osd_work_item *)item->queue->free;
|
||||
item->next = next;
|
||||
} while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
|
||||
osd_scalable_lock_release(item->queue->lock, lockslot);
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// effective_num_processors
|
||||
//============================================================
|
||||
|
||||
static int effective_num_processors(void)
|
||||
{
|
||||
int physprocs = osd_get_num_processors();
|
||||
|
||||
// osd_num_processors == 0 for 'auto'
|
||||
if (osd_num_processors > 0)
|
||||
{
|
||||
return MIN(4 * physprocs, osd_num_processors);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *procsoverride;
|
||||
int numprocs = 0;
|
||||
|
||||
// if the OSDPROCESSORS environment variable is set, use that value if valid
|
||||
// note that we permit more than the real number of processors for testing
|
||||
procsoverride = osd_getenv(ENV_PROCESSORS);
|
||||
if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0)
|
||||
return MIN(4 * physprocs, numprocs);
|
||||
|
||||
// otherwise, return the info from the system
|
||||
return physprocs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// worker_thread_entry
|
||||
//============================================================
|
||||
|
||||
static void *worker_thread_entry(void *param)
|
||||
{
|
||||
work_thread_info *thread = (work_thread_info *)param;
|
||||
osd_work_queue *queue = thread->queue;
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
void *arp = NewAutoreleasePool();
|
||||
#endif
|
||||
|
||||
// loop until we exit
|
||||
for ( ;; )
|
||||
{
|
||||
// block waiting for work or exit
|
||||
// bail on exit, and only wait if there are no pending items in queue
|
||||
if (queue->exiting)
|
||||
break;
|
||||
|
||||
if (!queue_has_list_items(queue))
|
||||
{
|
||||
begin_timing(thread->waittime);
|
||||
osd_event_wait(thread->wakeevent, INFINITE);
|
||||
end_timing(thread->waittime);
|
||||
}
|
||||
|
||||
if (queue->exiting)
|
||||
break;
|
||||
|
||||
// indicate that we are live
|
||||
atomic_exchange32(&thread->active, TRUE);
|
||||
atomic_increment32(&queue->livethreads);
|
||||
|
||||
// process work items
|
||||
for ( ;; )
|
||||
{
|
||||
osd_ticks_t stopspin;
|
||||
|
||||
// process as much as we can
|
||||
worker_thread_process(queue, thread);
|
||||
|
||||
// if we're a high frequency queue, spin for a while before giving up
|
||||
if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->list == NULL)
|
||||
{
|
||||
// spin for a while looking for more work
|
||||
begin_timing(thread->spintime);
|
||||
stopspin = osd_ticks() + SPIN_LOOP_TIME;
|
||||
|
||||
do {
|
||||
int spin = 10000;
|
||||
while (--spin && queue->list == NULL)
|
||||
osd_yield_processor();
|
||||
} while (queue->list == NULL && osd_ticks() < stopspin);
|
||||
end_timing(thread->spintime);
|
||||
}
|
||||
|
||||
// if nothing more, release the processor
|
||||
if (!queue_has_list_items(queue))
|
||||
break;
|
||||
add_to_stat(&queue->spinloops, 1);
|
||||
}
|
||||
|
||||
// decrement the live thread count
|
||||
atomic_exchange32(&thread->active, FALSE);
|
||||
atomic_decrement32(&queue->livethreads);
|
||||
}
|
||||
|
||||
#if defined(SDLMAME_MACOSX)
|
||||
ReleaseAutoreleasePool(arp);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
// worker_thread_process
|
||||
//============================================================
|
||||
|
||||
static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
|
||||
{
|
||||
int threadid = thread - queue->thread;
|
||||
|
||||
begin_timing(thread->runtime);
|
||||
|
||||
// loop until everything is processed
|
||||
while (true)
|
||||
{
|
||||
osd_work_item *item = NULL;
|
||||
|
||||
bool end_loop = false;
|
||||
|
||||
// use a critical section to synchronize the removal of items
|
||||
{
|
||||
INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
|
||||
if (queue->list == NULL)
|
||||
{
|
||||
end_loop = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// pull the item from the queue
|
||||
item = (osd_work_item *)queue->list;
|
||||
if (item != NULL)
|
||||
{
|
||||
queue->list = item->next;
|
||||
if (queue->list == NULL)
|
||||
queue->tailptr = (osd_work_item **)&queue->list;
|
||||
}
|
||||
}
|
||||
osd_scalable_lock_release(queue->lock, lockslot);
|
||||
}
|
||||
|
||||
if (end_loop)
|
||||
break;
|
||||
|
||||
// process non-NULL items
|
||||
if (item != NULL)
|
||||
{
|
||||
// call the callback and stash the result
|
||||
begin_timing(thread->actruntime);
|
||||
item->result = (*item->callback)(item->param, threadid);
|
||||
end_timing(thread->actruntime);
|
||||
|
||||
// decrement the item count after we are done
|
||||
atomic_decrement32(&queue->items);
|
||||
atomic_exchange32(&item->done, TRUE);
|
||||
add_to_stat(&thread->itemsdone, 1);
|
||||
|
||||
// if it's an auto-release item, release it
|
||||
if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
|
||||
osd_work_item_release(item);
|
||||
|
||||
// set the result and signal the event
|
||||
else
|
||||
{
|
||||
INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
|
||||
if (item->event != NULL)
|
||||
{
|
||||
osd_event_set(item->event);
|
||||
add_to_stat(&item->queue->setevents, 1);
|
||||
}
|
||||
osd_scalable_lock_release(item->queue->lock, lockslot);
|
||||
}
|
||||
|
||||
// if we removed an item and there's still work to do, bump the stats
|
||||
if (queue_has_list_items(queue))
|
||||
add_to_stat(&queue->extraitems, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// we don't need to set the doneevent for multi queues because they spin
|
||||
if (queue->waiting)
|
||||
{
|
||||
osd_event_set(queue->doneevent);
|
||||
add_to_stat(&queue->setevents, 1);
|
||||
}
|
||||
|
||||
end_timing(thread->runtime);
|
||||
}
|
||||
|
||||
bool queue_has_list_items(osd_work_queue *queue)
|
||||
{
|
||||
INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
|
||||
bool has_list_items = (queue->list != NULL);
|
||||
osd_scalable_lock_release(queue->lock, lockslot);
|
||||
return has_list_items;
|
||||
}
|
||||
#endif // SDLMAME_NOASM
|
@ -349,7 +349,7 @@ OSDCOREOBJS = \
|
||||
$(WINOBJ)/winutil.o \
|
||||
$(WINOBJ)/winclip.o \
|
||||
$(WINOBJ)/winsocket.o \
|
||||
$(WINOBJ)/winwork.o \
|
||||
$(OSDOBJ)/modules/sync/work_osd.o \
|
||||
$(WINOBJ)/winptty.o \
|
||||
$(WINOBJ)/winos.o \
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user