Previous 199869 Revisions Next

r34204 Tuesday 6th January, 2015 at 01:11:51 UTC by Angelo Salese
Fix compile
[/trunk]makefile
[src/mame/includes]senjyo.h
[src/osd/modules/sync]work_mini.c work_osd.c
[src/osd/osdmini]miniwork.c* osdmini.mak
[src/osd/sdl]sdl.mak sdlwork.c*
[src/osd/windows]windows.mak winwork.c*

trunk/makefile
r242715r242716
491491DEFS += -DMAME_DEBUG_FAST
492492endif
493493
494# add a define identifying the target osd
495494
496ifeq ($(OSD),sdl)
497DEFS += -DOSD_SDL
498else
499ifeq ($(OSD),windows)
500DEFS += -DOSD_WINDOWS
501else
502ifeq ($(OSD),osdmini)
503DEFS += -DOSD_MINI
504else
505$(error Unknown OSD)
506endif
507endif
508endif
509 
510495#-------------------------------------------------
511496# compile flags
512497# CCOMFLAGS are common flags
trunk/src/mame/includes/senjyo.h
r242715r242716
2929      m_palette(*this, "palette"),
3030      m_generic_paletteram_8(*this, "paletteram") { }
3131
32   int m_int_delay_kludge;
3332   UINT8 m_sound_cmd;
3433   INT16 *m_single_data;
3534   int m_single_rate;
r242715r242716
7473   DECLARE_WRITE8_MEMBER(senjyo_volume_w);
7574   DECLARE_WRITE_LINE_MEMBER(sound_line_clock);
7675   DECLARE_WRITE8_MEMBER(sound_cmd_w);
76   DECLARE_WRITE8_MEMBER(irq_ack_w);
7777   DECLARE_DRIVER_INIT(starfora);
7878   DECLARE_DRIVER_INIT(senjyo);
7979   DECLARE_DRIVER_INIT(starfore);
r242715r242716
8787   virtual void video_start();
8888   UINT32 screen_update_senjyo(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
8989   INTERRUPT_GEN_MEMBER(senjyo_interrupt);
90   DECLARE_WRITE8_MEMBER(irq_ctrl_w);
9190   DECLARE_READ8_MEMBER(pio_pa_r);
9291   void draw_bgbitmap(bitmap_ind16 &bitmap,const rectangle &cliprect);
9392   void draw_radar(bitmap_ind16 &bitmap,const rectangle &cliprect);
trunk/src/osd/modules/sync/work_mini.c
r242715r242716
1// license:BSD-3-Clause
2// copyright-holders:Aaron Giles
3//============================================================
4//
5//  miniwork.c - Minimal core work item functions
6//
7//============================================================
8
9#include "osdcore.h"
10#include <stdlib.h>
11
12
13//============================================================
14//  TYPE DEFINITIONS
15//============================================================
16
17struct osd_work_item
18{
19   void *result;
20};
21
22//============================================================
23//  GLOBAL VARIABLES
24//============================================================
25
26int osd_num_processors = 0;
27
28//============================================================
29//  osd_work_queue_alloc
30//============================================================
31
32osd_work_queue *osd_work_queue_alloc(int flags)
33{
34   // this minimal implementation doesn't need to keep any state
35   // so we just return a non-NULL pointer
36   return (osd_work_queue *)1;
37}
38
39
40//============================================================
41//  osd_work_queue_items
42//============================================================
43
44int osd_work_queue_items(osd_work_queue *queue)
45{
46   // we never have pending items
47   return 0;
48}
49
50
51//============================================================
52//  osd_work_queue_wait
53//============================================================
54
55int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
56{
57   // never anything to wait for, so do nothing
58   return TRUE;
59}
60
61
62//============================================================
63//  osd_work_queue_free
64//============================================================
65
66void osd_work_queue_free(osd_work_queue *queue)
67{
68   // never allocated anything, so nothing to do
69}
70
71
72//============================================================
73//  osd_work_item_queue
74//============================================================
75
76osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
77{
78   osd_work_item *item;
79   int itemnum;
80
81   // allocate memory to hold the result
82   item = (osd_work_item *)malloc(sizeof(*item));
83   if (item == NULL)
84      return NULL;
85
86   // loop over all requested items
87   for (itemnum = 0; itemnum < numitems; itemnum++)
88   {
89      // execute the call directly
90      item->result = (*callback)(parambase, 0);
91
92      // advance the param
93      parambase = (UINT8 *)parambase + paramstep;
94   }
95
96   // free the item if requested
97   if (flags & WORK_ITEM_FLAG_AUTO_RELEASE)
98   {
99      free(item);
100      item = NULL;
101   }
102   return item;
103}
104
105
106//============================================================
107//  osd_work_item_wait
108//============================================================
109
110int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
111{
112   // never anything to wait for, so do nothing
113   return TRUE;
114}
115
116
117//============================================================
118//  osd_work_item_result
119//============================================================
120
121void *osd_work_item_result(osd_work_item *item)
122{
123   return item->result;
124}
125
126
127//============================================================
128//  osd_work_item_release
129//============================================================
130
131void osd_work_item_release(osd_work_item *item)
132{
133   free(item);
134}
trunk/src/osd/modules/sync/work_osd.c
r242715r242716
1// license:BSD-3-Clause
2// copyright-holders:Aaron Giles
3//============================================================
4//
5//  sdlwork.c - SDL OSD core work item functions
6//
7//  Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
8//  Visit http://mamedev.org for licensing and usage restrictions.
9//
10//============================================================
11
12#if defined(OSD_WINDOWS)
13// standard windows headers
14#define WIN32_LEAN_AND_MEAN
15#include <windows.h>
16#include <process.h>
17#include <tchar.h>
18#include <stdlib.h>
19
20#ifdef __GNUC__
21#include <stdint.h>
22#endif
23#endif
24
25// MAME headers
26#include "osdcore.h"
27
28#include "modules/sync/osdsync.h"
29
30#if defined(OSD_WINDOWS)
31#include "winos.h"
32#elif defined(OSD_SDL)
33#include "sdlos.h"
34typedef void *PVOID;
35#endif
36
37#include "eminline.h"
38
39#if defined(SDLMAME_MACOSX)
40#include "osxutils.h"
41#endif
42
43
44//============================================================
45//  DEBUGGING
46//============================================================
47
48#define KEEP_STATISTICS         (0)
49
50//============================================================
51//  PARAMETERS
52//============================================================
53
54#define ENV_PROCESSORS               "OSDPROCESSORS"
55
56#if defined(OSD_WINDOWS)
57#define SPIN_LOOP_TIME          (osd_ticks_per_second() / 1000)
58#else
59#define INFINITE                (osd_ticks_per_second() *  (osd_ticks_t) 10000)
60#define SPIN_LOOP_TIME          (osd_ticks_per_second() / 10000)
61#endif
62
63//============================================================
64//  MACROS
65//============================================================
66
67#if KEEP_STATISTICS
68#define add_to_stat(v,x)        do { atomic_add32((v), (x)); } while (0)
69#define begin_timing(v)         do { (v) -= get_profile_ticks(); } while (0)
70#define end_timing(v)           do { (v) += get_profile_ticks(); } while (0)
71#else
72#define add_to_stat(v,x)        do { } while (0)
73#define begin_timing(v)         do { } while (0)
74#define end_timing(v)           do { } while (0)
75#endif
76
77#if __GNUC__ && defined(__i386__) && !defined(__x86_64)
78#undef YieldProcessor
79#endif
80
81#ifndef YieldProcessor
82#ifdef __GNUC__
83INLINE void osd_yield_processor(void)
84{
85   __asm__ __volatile__ ( "rep; nop" );
86}
87#else
88INLINE void osd_yield_processor(void)
89{
90   __asm { rep nop }
91}
92#endif
93#else
94#define osd_yield_processor YieldProcessor
95#endif
96
97
98
99//============================================================
100//  TYPE DEFINITIONS
101//============================================================
102
103struct work_thread_info
104{
105   osd_work_queue *    queue;          // pointer back to the queue
106   osd_thread *        handle;         // handle to the thread
107   osd_event *         wakeevent;      // wake event for the thread
108   volatile INT32      active;         // are we actively processing work?
109
110#if KEEP_STATISTICS
111   INT32               itemsdone;
112   osd_ticks_t         actruntime;
113   osd_ticks_t         runtime;
114   osd_ticks_t         spintime;
115   osd_ticks_t         waittime;
116#endif
117};
118
119
120struct osd_work_queue
121{
122   osd_scalable_lock * lock;           // lock for protecting the queue
123   osd_work_item * volatile list;      // list of items in the queue
124   osd_work_item ** volatile tailptr;  // pointer to the tail pointer of work items in the queue
125   osd_work_item * volatile free;      // free list of work items
126   volatile INT32      items;          // items in the queue
127   volatile INT32      livethreads;    // number of live threads
128   volatile INT32      waiting;        // is someone waiting on the queue to complete?
129   volatile INT32      exiting;        // should the threads exit on their next opportunity?
130   UINT32              threads;        // number of threads in this queue
131   UINT32              flags;          // creation flags
132   work_thread_info *  thread;         // array of thread information
133   osd_event   *       doneevent;      // event signalled when work is complete
134
135#if KEEP_STATISTICS
136   volatile INT32      itemsqueued;    // total items queued
137   volatile INT32      setevents;      // number of times we called SetEvent
138   volatile INT32      extraitems;     // how many extra items we got after the first in the queue loop
139   volatile INT32      spinloops;      // how many times spinning bought us more items
140#endif
141};
142
143
144struct osd_work_item
145{
146   osd_work_item *     next;           // pointer to next item
147   osd_work_queue *    queue;          // pointer back to the owning queue
148   osd_work_callback   callback;       // callback function
149   void *              param;          // callback parameter
150   void *              result;         // callback result
151   osd_event *         event;          // event signalled when complete
152   UINT32              flags;          // creation flags
153   volatile INT32      done;           // is the item done?
154};
155
156//============================================================
157//  GLOBAL VARIABLES
158//============================================================
159
160int osd_num_processors = 0;
161
162//============================================================
163//  FUNCTION PROTOTYPES
164//============================================================
165
166static int effective_num_processors(void);
167static void * worker_thread_entry(void *param);
168static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
169static bool queue_has_list_items(osd_work_queue *queue);
170
171
172//============================================================
173//  osd_work_queue_alloc
174//============================================================
175
176osd_work_queue *osd_work_queue_alloc(int flags)
177{
178   int numprocs = effective_num_processors();
179   osd_work_queue *queue;
180   int threadnum;
181   char *osdworkqueuemaxthreads = osd_getenv("OSDWORKQUEUEMAXTHREADS");
182
183   // allocate a new queue
184   queue = (osd_work_queue *)osd_malloc(sizeof(*queue));
185   if (queue == NULL)
186      goto error;
187   memset(queue, 0, sizeof(*queue));
188
189   // initialize basic queue members
190   queue->tailptr = (osd_work_item **)&queue->list;
191   queue->flags = flags;
192
193   // allocate events for the queue
194   queue->doneevent = osd_event_alloc(TRUE, TRUE);     // manual reset, signalled
195   if (queue->doneevent == NULL)
196      goto error;
197
198   // initialize the critical section
199   queue->lock = osd_scalable_lock_alloc();
200   if (queue->lock == NULL)
201      goto error;
202
203   // determine how many threads to create...
204   // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
205   if (numprocs == 1)
206      queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
207#if defined(OSD_WINDOWS)
208   // on an n-CPU system, create n threads for multi queues, and 1 thread for everything else
209   else
210      queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? numprocs : 1;
211#else
212   // on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
213   else
214      queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
215#endif
216
217   if (osdworkqueuemaxthreads != NULL && sscanf(osdworkqueuemaxthreads, "%d", &threadnum) == 1 && queue->threads > threadnum)
218      queue->threads = threadnum;
219
220#if defined(OSD_WINDOWS)
221   // multi-queues with high frequency items should top out at 4 for now
222   // since we have scaling problems above that
223   if ((flags & WORK_QUEUE_FLAG_HIGH_FREQ) && queue->threads > 1)
224      queue->threads = MIN(queue->threads - 1, 4);
225#endif
226
227   // clamp to the maximum
228   queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
229
230   // allocate memory for thread array (+1 to count the calling thread)
231   queue->thread = (work_thread_info *)osd_malloc_array((queue->threads + 1) * sizeof(queue->thread[0]));
232   if (queue->thread == NULL)
233      goto error;
234   memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
235
236   // iterate over threads
237   for (threadnum = 0; threadnum < queue->threads; threadnum++)
238   {
239      work_thread_info *thread = &queue->thread[threadnum];
240
241      // set a pointer back to the queue
242      thread->queue = queue;
243
244      // create the per-thread wake event
245      thread->wakeevent = osd_event_alloc(FALSE, FALSE);  // auto-reset, not signalled
246      if (thread->wakeevent == NULL)
247         goto error;
248
249      // create the thread
250      thread->handle = osd_thread_create(worker_thread_entry, thread);
251      if (thread->handle == NULL)
252         goto error;
253
254      // set its priority: I/O threads get high priority because they are assumed to be
255      // blocked most of the time; other threads just match the creator's priority
256      if (flags & WORK_QUEUE_FLAG_IO)
257         osd_thread_adjust_priority(thread->handle, 1);
258      else
259         osd_thread_adjust_priority(thread->handle, 0);
260   }
261
262   // start a timer going for "waittime" on the main thread
263   begin_timing(queue->thread[queue->threads].waittime);
264   return queue;
265
266error:
267   osd_work_queue_free(queue);
268   return NULL;
269}
270
271
272//============================================================
273//  osd_work_queue_items
274//============================================================
275
276int osd_work_queue_items(osd_work_queue *queue)
277{
278   // return the number of items currently in the queue
279   return queue->items;
280}
281
282
283//============================================================
284//  osd_work_queue_wait
285//============================================================
286
287int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
288{
289   // if no threads, no waiting
290   if (queue->threads == 0)
291      return TRUE;
292
293   // if no items, we're done
294   if (queue->items == 0)
295      return TRUE;
296
297   // if this is a multi queue, help out rather than doing nothing
298   if (queue->flags & WORK_QUEUE_FLAG_MULTI)
299   {
300      work_thread_info *thread = &queue->thread[queue->threads];
301
302      end_timing(thread->waittime);
303
304      // process what we can as a worker thread
305      worker_thread_process(queue, thread);
306
307      // if we're a high frequency queue, spin until done
308      if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->items != 0)
309      {
310         osd_ticks_t stopspin = osd_ticks() + timeout;
311
312         // spin until we're done
313         begin_timing(thread->spintime);
314
315#if defined(OSD_WINDOWS)
316         while (queue->items != 0 && osd_ticks() < stopspin)
317            osd_yield_processor();
318#else
319         do {
320            int spin = 10000;
321            while (--spin && queue->items != 0)
322               osd_yield_processor();
323         } while (queue->items != 0 && osd_ticks() < stopspin);
324#endif
325
326         end_timing(thread->spintime);
327
328         begin_timing(thread->waittime);
329         return (queue->items == 0);
330      }
331      begin_timing(thread->waittime);
332   }
333
334   // reset our done event and double-check the items before waiting
335   osd_event_reset(queue->doneevent);
336   atomic_exchange32(&queue->waiting, TRUE);
337   if (queue->items != 0)
338      osd_event_wait(queue->doneevent, timeout);
339   atomic_exchange32(&queue->waiting, FALSE);
340
341   // return TRUE if we actually hit 0
342   return (queue->items == 0);
343}
344
345
346//============================================================
347//  osd_work_queue_free
348//============================================================
349
350void osd_work_queue_free(osd_work_queue *queue)
351{
352   // if we have threads, clean them up
353   if (queue->thread != NULL)
354   {
355      int threadnum;
356
357      // stop the timer for "waittime" on the main thread
358      end_timing(queue->thread[queue->threads].waittime);
359
360      // signal all the threads to exit
361      atomic_exchange32(&queue->exiting, TRUE);
362      for (threadnum = 0; threadnum < queue->threads; threadnum++)
363      {
364         work_thread_info *thread = &queue->thread[threadnum];
365         if (thread->wakeevent != NULL)
366            osd_event_set(thread->wakeevent);
367      }
368
369      // wait for all the threads to go away
370      for (threadnum = 0; threadnum < queue->threads; threadnum++)
371      {
372         work_thread_info *thread = &queue->thread[threadnum];
373
374         // block on the thread going away, then close the handle
375         if (thread->handle != NULL)
376         {
377            osd_thread_wait_free(thread->handle);
378         }
379
380         // clean up the wake event
381         if (thread->wakeevent != NULL)
382            osd_event_free(thread->wakeevent);
383      }
384
385#if KEEP_STATISTICS
386      // output per-thread statistics
387      for (threadnum = 0; threadnum <= queue->threads; threadnum++)
388      {
389         work_thread_info *thread = &queue->thread[threadnum];
390         osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
391         printf("Thread %d:  items=%9d run=%5.2f%% (%5.2f%%)  spin=%5.2f%%  wait/other=%5.2f%% total=%9d\n",
392               threadnum, thread->itemsdone,
393               (double)thread->runtime * 100.0 / (double)total,
394               (double)thread->actruntime * 100.0 / (double)total,
395               (double)thread->spintime * 100.0 / (double)total,
396               (double)thread->waittime * 100.0 / (double)total,
397               (UINT32) total);
398      }
399#endif
400   }
401
402   // free the list
403   if (queue->thread != NULL)
404      osd_free(queue->thread);
405
406   // free all the events
407   if (queue->doneevent != NULL)
408      osd_event_free(queue->doneevent);
409
410   // free all items in the free list
411   while (queue->free != NULL)
412   {
413      osd_work_item *item = (osd_work_item *)queue->free;
414      queue->free = item->next;
415      if (item->event != NULL)
416         osd_event_free(item->event);
417      osd_free(item);
418   }
419
420   // free all items in the active list
421   while (queue->list != NULL)
422   {
423      osd_work_item *item = (osd_work_item *)queue->list;
424      queue->list = item->next;
425      if (item->event != NULL)
426         osd_event_free(item->event);
427      osd_free(item);
428   }
429
430#if KEEP_STATISTICS
431   printf("Items queued   = %9d\n", queue->itemsqueued);
432   printf("SetEvent calls = %9d\n", queue->setevents);
433   printf("Extra items    = %9d\n", queue->extraitems);
434   printf("Spin loops     = %9d\n", queue->spinloops);
435#endif
436
437   osd_scalable_lock_free(queue->lock);
438   // free the queue itself
439   osd_free(queue);
440}
441
442
443//============================================================
444//  osd_work_item_queue_multiple
445//============================================================
446
447osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
448{
449   osd_work_item *itemlist = NULL, *lastitem = NULL;
450   osd_work_item **item_tailptr = &itemlist;
451   INT32 lockslot;
452   int itemnum;
453
454   // loop over items, building up a local list of work
455   for (itemnum = 0; itemnum < numitems; itemnum++)
456   {
457      osd_work_item *item;
458
459      // first allocate a new work item; try the free list first
460      INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
461      do
462      {
463         item = (osd_work_item *)queue->free;
464      } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
465      osd_scalable_lock_release(queue->lock, lockslot);
466
467      // if nothing, allocate something new
468      if (item == NULL)
469      {
470         // allocate the item
471         item = (osd_work_item *)osd_malloc(sizeof(*item));
472         if (item == NULL)
473            return NULL;
474         item->event = NULL;
475         item->queue = queue;
476         item->done = FALSE;
477      }
478      else
479      {
480         atomic_exchange32(&item->done, FALSE); // needs to be set this way to prevent data race/usage of uninitialized memory on Linux
481      }
482
483      // fill in the basics
484      item->next = NULL;
485      item->callback = callback;
486      item->param = parambase;
487      item->result = NULL;
488      item->flags = flags;
489
490      // advance to the next
491      lastitem = item;
492      *item_tailptr = item;
493      item_tailptr = &item->next;
494      parambase = (UINT8 *)parambase + paramstep;
495   }
496
497   // enqueue the whole thing within the critical section
498   lockslot = osd_scalable_lock_acquire(queue->lock);
499   *queue->tailptr = itemlist;
500   queue->tailptr = item_tailptr;
501   osd_scalable_lock_release(queue->lock, lockslot);
502
503   // increment the number of items in the queue
504   atomic_add32(&queue->items, numitems);
505   add_to_stat(&queue->itemsqueued, numitems);
506
507   // look for free threads to do the work
508   if (queue->livethreads < queue->threads)
509   {
510      int threadnum;
511
512      // iterate over all the threads
513      for (threadnum = 0; threadnum < queue->threads; threadnum++)
514      {
515         work_thread_info *thread = &queue->thread[threadnum];
516
517         // if this thread is not active, wake him up
518         if (!thread->active)
519         {
520            osd_event_set(thread->wakeevent);
521            add_to_stat(&queue->setevents, 1);
522
523            // for non-shared, the first one we find is good enough
524            if (--numitems == 0)
525               break;
526         }
527      }
528   }
529
530   // if no threads, run the queue now on this thread
531   if (queue->threads == 0)
532   {
533      end_timing(queue->thread[0].waittime);
534      worker_thread_process(queue, &queue->thread[0]);
535      begin_timing(queue->thread[0].waittime);
536   }
537   // only return the item if it won't get released automatically
538   return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
539}
540
541
542//============================================================
543//  osd_work_item_wait
544//============================================================
545
546int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
547{
548   // if we're done already, just return
549   if (item->done)
550      return TRUE;
551
552   // if we don't have an event, create one
553   if (item->event == NULL)
554   {
555      INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
556      item->event = osd_event_alloc(TRUE, FALSE);     // manual reset, not signalled
557      osd_scalable_lock_release(item->queue->lock, lockslot);
558   }
559   else
560      osd_event_reset(item->event);
561
562   // if we don't have an event, we need to spin (shouldn't ever really happen)
563   if (item->event == NULL)
564   {
565      osd_ticks_t stopspin = osd_ticks() + timeout;
566#if defined(OSD_WINDOWS)
567      while (!item->done && osd_ticks() < stopspin)
568         osd_yield_processor();
569#else
570      do {
571         int spin = 10000;
572         while (--spin && !item->done)
573            osd_yield_processor();
574      } while (!item->done && osd_ticks() < stopspin);
575#endif
576   }
577
578   // otherwise, block on the event until done
579   else if (!item->done)
580      osd_event_wait(item->event, timeout);
581
582   // return TRUE if the refcount actually hit 0
583   return item->done;
584}
585
586
587//============================================================
588//  osd_work_item_result
589//============================================================
590
591void *osd_work_item_result(osd_work_item *item)
592{
593   return item->result;
594}
595
596
597//============================================================
598//  osd_work_item_release
599//============================================================
600
601void osd_work_item_release(osd_work_item *item)
602{
603   osd_work_item *next;
604
605   // make sure we're done first
606   osd_work_item_wait(item, 100 * osd_ticks_per_second());
607
608   // add us to the free list on our queue
609   INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
610   do
611   {
612      next = (osd_work_item *)item->queue->free;
613      item->next = next;
614   } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
615   osd_scalable_lock_release(item->queue->lock, lockslot);
616}
617
618
619//============================================================
620//  effective_num_processors
621//============================================================
622
623static int effective_num_processors(void)
624{
625   int physprocs = osd_get_num_processors();
626
627   // osd_num_processors == 0 for 'auto'
628   if (osd_num_processors > 0)
629   {
630      return MIN(4 * physprocs, osd_num_processors);
631   }
632   else
633   {
634      char *procsoverride;
635      int numprocs = 0;
636
637      // if the OSDPROCESSORS environment variable is set, use that value if valid
638      // note that we permit more than the real number of processors for testing
639      procsoverride = osd_getenv(ENV_PROCESSORS);
640      if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0)
641         return MIN(4 * physprocs, numprocs);
642
643      // otherwise, return the info from the system
644      return physprocs;
645   }
646}
647
648
649//============================================================
650//  worker_thread_entry
651//============================================================
652
653static void *worker_thread_entry(void *param)
654{
655   work_thread_info *thread = (work_thread_info *)param;
656   osd_work_queue *queue = thread->queue;
657
658   #if defined(SDLMAME_MACOSX)
659   void *arp = NewAutoreleasePool();
660   #endif
661
662   // loop until we exit
663   for ( ;; )
664   {
665      // block waiting for work or exit
666      // bail on exit, and only wait if there are no pending items in queue
667      if (queue->exiting)
668         break;
669
670      if (!queue_has_list_items(queue))
671      {
672         begin_timing(thread->waittime);
673         osd_event_wait(thread->wakeevent, INFINITE);
674         end_timing(thread->waittime);
675      }
676
677      if (queue->exiting)
678         break;
679
680      // indicate that we are live
681      atomic_exchange32(&thread->active, TRUE);
682      atomic_increment32(&queue->livethreads);
683
684      // process work items
685      for ( ;; )
686      {
687         osd_ticks_t stopspin;
688
689         // process as much as we can
690         worker_thread_process(queue, thread);
691
692         // if we're a high frequency queue, spin for a while before giving up
693         if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->list == NULL)
694         {
695            // spin for a while looking for more work
696            begin_timing(thread->spintime);
697            stopspin = osd_ticks() + SPIN_LOOP_TIME;
698
699#if defined(OSD_WINDOWS)
700            while (queue->list == NULL && osd_ticks() < stopspin)
701               osd_yield_processor();
702#else
703            do {
704               int spin = 10000;
705               while (--spin && queue->list == NULL)
706                  osd_yield_processor();
707            } while (queue->list == NULL && osd_ticks() < stopspin);
708#endif
709
710            end_timing(thread->spintime);
711         }
712
713         // if nothing more, release the processor
714         if (!queue_has_list_items(queue))
715            break;
716         add_to_stat(&queue->spinloops, 1);
717      }
718
719      // decrement the live thread count
720      atomic_exchange32(&thread->active, FALSE);
721      atomic_decrement32(&queue->livethreads);
722   }
723
724   #if defined(SDLMAME_MACOSX)
725   ReleaseAutoreleasePool(arp);
726   #endif
727
728   return NULL;
729}
730
731
732//============================================================
733//  worker_thread_process
734//============================================================
735
736static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
737{
738   int threadid = thread - queue->thread;
739
740   begin_timing(thread->runtime);
741
742   // loop until everything is processed
743   while (true)
744   {
745      osd_work_item *item = NULL;
746
747      bool end_loop = false;
748
749      // use a critical section to synchronize the removal of items
750      {
751         INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
752         if (queue->list == NULL)
753         {
754            end_loop = true;
755         }
756         else
757         {
758            // pull the item from the queue
759            item = (osd_work_item *)queue->list;
760            if (item != NULL)
761            {
762               queue->list = item->next;
763               if (queue->list == NULL)
764                  queue->tailptr = (osd_work_item **)&queue->list;
765            }
766         }
767         osd_scalable_lock_release(queue->lock, lockslot);
768      }
769
770      if (end_loop)
771         break;
772
773      // process non-NULL items
774      if (item != NULL)
775      {
776         // call the callback and stash the result
777         begin_timing(thread->actruntime);
778         item->result = (*item->callback)(item->param, threadid);
779         end_timing(thread->actruntime);
780
781         // decrement the item count after we are done
782         atomic_decrement32(&queue->items);
783         atomic_exchange32(&item->done, TRUE);
784         add_to_stat(&thread->itemsdone, 1);
785
786         // if it's an auto-release item, release it
787         if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
788            osd_work_item_release(item);
789
790         // set the result and signal the event
791         else
792         {
793            INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
794            if (item->event != NULL)
795            {
796               osd_event_set(item->event);
797               add_to_stat(&item->queue->setevents, 1);
798            }
799            osd_scalable_lock_release(item->queue->lock, lockslot);
800         }
801
802         // if we removed an item and there's still work to do, bump the stats
803         if (queue_has_list_items(queue))
804            add_to_stat(&queue->extraitems, 1);
805      }
806   }
807
808   // we don't need to set the doneevent for multi queues because they spin
809   if (queue->waiting)
810   {
811      osd_event_set(queue->doneevent);
812      add_to_stat(&queue->setevents, 1);
813   }
814
815   end_timing(thread->runtime);
816}
817
818bool queue_has_list_items(osd_work_queue *queue)
819{
820   INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
821   bool has_list_items = (queue->list != NULL);
822   osd_scalable_lock_release(queue->lock, lockslot);
823   return has_list_items;
824}
trunk/src/osd/osdmini/miniwork.c
r0r242716
1// license:BSD-3-Clause
2// copyright-holders:Aaron Giles
3//============================================================
4//
5//  miniwork.c - Minimal core work item functions
6//
7//============================================================
8
9#include "osdcore.h"
10#include <stdlib.h>
11
12
13//============================================================
14//  TYPE DEFINITIONS
15//============================================================
16
17struct osd_work_item
18{
19   void *result;
20};
21
22//============================================================
23//  GLOBAL VARIABLES
24//============================================================
25
26int osd_num_processors = 0;
27
28//============================================================
29//  osd_work_queue_alloc
30//============================================================
31
32osd_work_queue *osd_work_queue_alloc(int flags)
33{
34   // this minimal implementation doesn't need to keep any state
35   // so we just return a non-NULL pointer
36   return (osd_work_queue *)1;
37}
38
39
40//============================================================
41//  osd_work_queue_items
42//============================================================
43
44int osd_work_queue_items(osd_work_queue *queue)
45{
46   // we never have pending items
47   return 0;
48}
49
50
51//============================================================
52//  osd_work_queue_wait
53//============================================================
54
55int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
56{
57   // never anything to wait for, so do nothing
58   return TRUE;
59}
60
61
62//============================================================
63//  osd_work_queue_free
64//============================================================
65
66void osd_work_queue_free(osd_work_queue *queue)
67{
68   // never allocated anything, so nothing to do
69}
70
71
72//============================================================
73//  osd_work_item_queue
74//============================================================
75
76osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
77{
78   osd_work_item *item;
79   int itemnum;
80
81   // allocate memory to hold the result
82   item = (osd_work_item *)malloc(sizeof(*item));
83   if (item == NULL)
84      return NULL;
85
86   // loop over all requested items
87   for (itemnum = 0; itemnum < numitems; itemnum++)
88   {
89      // execute the call directly
90      item->result = (*callback)(parambase, 0);
91
92      // advance the param
93      parambase = (UINT8 *)parambase + paramstep;
94   }
95
96   // free the item if requested
97   if (flags & WORK_ITEM_FLAG_AUTO_RELEASE)
98   {
99      free(item);
100      item = NULL;
101   }
102   return item;
103}
104
105
106//============================================================
107//  osd_work_item_wait
108//============================================================
109
110int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
111{
112   // never anything to wait for, so do nothing
113   return TRUE;
114}
115
116
117//============================================================
118//  osd_work_item_result
119//============================================================
120
121void *osd_work_item_result(osd_work_item *item)
122{
123   return item->result;
124}
125
126
127//============================================================
128//  osd_work_item_release
129//============================================================
130
131void osd_work_item_release(osd_work_item *item)
132{
133   free(item);
134}
trunk/src/osd/osdmini/osdmini.mak
r242715r242716
4545MINISRC = $(SRC)/osd/$(OSD)
4646MINIOBJ = $(OBJ)/osd/$(OSD)
4747
48OSDSRC = $(SRC)/osd
49OSDOBJ = $(OBJ)/osd
48OBJDIRS += $(MINIOBJ)
5049
51OBJDIRS += $(MINIOBJ) $(OSDOBJ)/modules/sync
5250
5351
54
5552#-------------------------------------------------
5653# OSD core library
5754#-------------------------------------------------
r242715r242716
6259   $(MINIOBJ)/minimisc.o \
6360   $(MINIOBJ)/minisync.o \
6461   $(MINIOBJ)/minitime.o \
65   $(OSDOBJ)/modules/sync/work_mini.o \
62   $(MINIOBJ)/miniwork.o \
6663
64
65
6766#-------------------------------------------------
6867# OSD mini library
6968#-------------------------------------------------
trunk/src/osd/sdl/sdl.mak
r242715r242716
405405   $(SDLOBJ)/sdlsocket.o   \
406406   $(SDLOBJ)/sdlmisc_$(BASE_TARGETOS).o    \
407407   $(SDLOBJ)/sdlos_$(SDLOS_TARGETOS).o \
408   $(OSDOBJ)/modules/sync/sync_$(SYNC_IMPLEMENTATION).o
408   $(OSDOBJ)/modules/sync/sync_$(SYNC_IMPLEMENTATION).o     \
409   $(SDLOBJ)/sdlwork.o
409410
410ifdef NOASM
411OSDCOREOBJS += $(OSDOBJ)/modules/sync/work_mini.o
412else
413OSDCOREOBJS += $(OSDOBJ)/modules/sync/work_osd.o
414endif
415
416411# any "main" must be in LIBOSD or else the build will fail!
417412# for the windows build, we just add it to libocore as well.
418413OSDOBJS = \
trunk/src/osd/sdl/sdlwork.c
r0r242716
1//============================================================
2//
3//  sdlwork.c - SDL OSD core work item functions
4//
5//  Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
6//  Visit http://mamedev.org for licensing and usage restrictions.
7//
8//  SDLMAME by Olivier Galibert and R. Belmont
9//
10//============================================================
11
12#if defined(SDLMAME_NOASM)
13
14#include "../osdmini/miniwork.c"
15
16#else
17
18#include "osdcore.h"
19#include "osinline.h"
20
21#include "modules/sync/osdsync.h"
22#include "sdlos.h"
23
24#include "eminline.h"
25
26#if defined(SDLMAME_MACOSX)
27#include "osxutils.h"
28#endif
29
30//============================================================
31//  DEBUGGING
32//============================================================
33
34#define KEEP_STATISTICS         (0)
35
36//============================================================
37//  PARAMETERS
38//============================================================
39
40#define ENV_PROCESSORS               "OSDPROCESSORS"
41#define ENV_WORKQUEUEMAXTHREADS      "OSDWORKQUEUEMAXTHREADS"
42
43#define INFINITE                (osd_ticks_per_second() *  (osd_ticks_t) 10000)
44#define SPIN_LOOP_TIME          (osd_ticks_per_second() / 10000)
45
46
47//============================================================
48//  MACROS
49//============================================================
50
51#if KEEP_STATISTICS
52#define add_to_stat(v,x)        do { atomic_add32((v), (x)); } while (0)
53#define begin_timing(v)         do { (v) -= get_profile_ticks(); } while (0)
54#define end_timing(v)           do { (v) += get_profile_ticks(); } while (0)
55#else
56#define add_to_stat(v,x)        do { } while (0)
57#define begin_timing(v)         do { } while (0)
58#define end_timing(v)           do { } while (0)
59#endif
60
61//============================================================
62//  TYPE DEFINITIONS
63//============================================================
64
65struct work_thread_info
66{
67   osd_work_queue *    queue;          // pointer back to the queue
68   osd_thread *        handle;         // handle to the thread
69   osd_event *         wakeevent;      // wake event for the thread
70   volatile INT32      active;         // are we actively processing work?
71
72#if KEEP_STATISTICS
73   INT32               itemsdone;
74   osd_ticks_t         actruntime;
75   osd_ticks_t         runtime;
76   osd_ticks_t         spintime;
77   osd_ticks_t         waittime;
78#endif
79};
80
81
82struct osd_work_queue
83{
84   osd_scalable_lock * lock;           // lock for protecting the queue
85   osd_work_item * volatile list;      // list of items in the queue
86   osd_work_item ** volatile tailptr;  // pointer to the tail pointer of work items in the queue
87   osd_work_item * volatile free;      // free list of work items
88   volatile INT32      items;          // items in the queue
89   volatile INT32      livethreads;    // number of live threads
90   volatile INT32      waiting;        // is someone waiting on the queue to complete?
91   volatile INT32      exiting;        // should the threads exit on their next opportunity?
92   UINT32              threads;        // number of threads in this queue
93   UINT32              flags;          // creation flags
94   work_thread_info *  thread;         // array of thread information
95   osd_event   *       doneevent;      // event signalled when work is complete
96
97#if KEEP_STATISTICS
98   volatile INT32      itemsqueued;    // total items queued
99   volatile INT32      setevents;      // number of times we called SetEvent
100   volatile INT32      extraitems;     // how many extra items we got after the first in the queue loop
101   volatile INT32      spinloops;      // how many times spinning bought us more items
102#endif
103};
104
105
106struct osd_work_item
107{
108   osd_work_item *     next;           // pointer to next item
109   osd_work_queue *    queue;          // pointer back to the owning queue
110   osd_work_callback   callback;       // callback function
111   void *              param;          // callback parameter
112   void *              result;         // callback result
113   osd_event *         event;          // event signalled when complete
114   UINT32              flags;          // creation flags
115   volatile INT32      done;           // is the item done?
116};
117
118typedef void *PVOID;
119
120//============================================================
121//  GLOBAL VARIABLES
122//============================================================
123
124int osd_num_processors = 0;
125
126//============================================================
127//  FUNCTION PROTOTYPES
128//============================================================
129
130static int effective_num_processors(void);
131static void * worker_thread_entry(void *param);
132static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
133static bool queue_has_list_items(osd_work_queue *queue);
134
135
136//============================================================
137//  osd_work_queue_alloc
138//============================================================
139
140osd_work_queue *osd_work_queue_alloc(int flags)
141{
142   int numprocs = effective_num_processors();
143   osd_work_queue *queue;
144   int threadnum;
145   char *osdworkqueuemaxthreads = osd_getenv("OSDWORKQUEUEMAXTHREADS");
146
147   // allocate a new queue
148   queue = (osd_work_queue *)osd_malloc(sizeof(*queue));
149   if (queue == NULL)
150      goto error;
151   memset(queue, 0, sizeof(*queue));
152
153   // initialize basic queue members
154   queue->tailptr = (osd_work_item **)&queue->list;
155   queue->flags = flags;
156
157   // allocate events for the queue
158   queue->doneevent = osd_event_alloc(TRUE, TRUE);     // manual reset, signalled
159   if (queue->doneevent == NULL)
160      goto error;
161
162   // initialize the critical section
163   queue->lock = osd_scalable_lock_alloc();
164   if (queue->lock == NULL)
165      goto error;
166
167   // determine how many threads to create...
168   // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
169   if (numprocs == 1)
170      queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
171
172   // on an n-CPU system, create (n-1) threads for multi queues, and 1 thread for everything else
173   else
174      queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? (numprocs - 1) : 1;
175
176   if (osdworkqueuemaxthreads != NULL && sscanf(osdworkqueuemaxthreads, "%d", &threadnum) == 1 && queue->threads > threadnum)
177      queue->threads = threadnum;
178
179
180   // clamp to the maximum
181   queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
182
183   // allocate memory for thread array (+1 to count the calling thread)
184   queue->thread = (work_thread_info *)osd_malloc_array((queue->threads + 1) * sizeof(queue->thread[0]));
185   if (queue->thread == NULL)
186      goto error;
187   memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
188
189   // iterate over threads
190   for (threadnum = 0; threadnum < queue->threads; threadnum++)
191   {
192      work_thread_info *thread = &queue->thread[threadnum];
193
194      // set a pointer back to the queue
195      thread->queue = queue;
196
197      // create the per-thread wake event
198      thread->wakeevent = osd_event_alloc(FALSE, FALSE);  // auto-reset, not signalled
199      if (thread->wakeevent == NULL)
200         goto error;
201
202      // create the thread
203      thread->handle = osd_thread_create(worker_thread_entry, thread);
204      if (thread->handle == NULL)
205         goto error;
206
207      // set its priority: I/O threads get high priority because they are assumed to be
208      // blocked most of the time; other threads just match the creator's priority
209      if (flags & WORK_QUEUE_FLAG_IO)
210         osd_thread_adjust_priority(thread->handle, 1);
211      else
212         osd_thread_adjust_priority(thread->handle, 0);
213   }
214
215   // start a timer going for "waittime" on the main thread
216   begin_timing(queue->thread[queue->threads].waittime);
217   return queue;
218
219error:
220   osd_work_queue_free(queue);
221   return NULL;
222}
223
224
225//============================================================
226//  osd_work_queue_items
227//============================================================
228
229int osd_work_queue_items(osd_work_queue *queue)
230{
231   // return the number of items currently in the queue
232   return queue->items;
233}
234
235
236//============================================================
237//  osd_work_queue_wait
238//============================================================
239
240int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
241{
242   // if no threads, no waiting
243   if (queue->threads == 0)
244      return TRUE;
245
246   // if no items, we're done
247   if (queue->items == 0)
248      return TRUE;
249
250   // if this is a multi queue, help out rather than doing nothing
251   if (queue->flags & WORK_QUEUE_FLAG_MULTI)
252   {
253      work_thread_info *thread = &queue->thread[queue->threads];
254
255      end_timing(thread->waittime);
256
257      // process what we can as a worker thread
258      worker_thread_process(queue, thread);
259
260      // if we're a high frequency queue, spin until done
261      if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->items != 0)
262      {
263         osd_ticks_t stopspin = osd_ticks() + timeout;
264
265         // spin until we're done
266         begin_timing(thread->spintime);
267
268         do {
269            int spin = 10000;
270            while (--spin && queue->items != 0)
271               osd_yield_processor();
272         } while (queue->items != 0 && osd_ticks() < stopspin);
273         end_timing(thread->spintime);
274
275         begin_timing(thread->waittime);
276         return (queue->items == 0);
277      }
278      begin_timing(thread->waittime);
279   }
280
281   // reset our done event and double-check the items before waiting
282   osd_event_reset(queue->doneevent);
283   atomic_exchange32(&queue->waiting, TRUE);
284   if (queue->items != 0)
285      osd_event_wait(queue->doneevent, timeout);
286   atomic_exchange32(&queue->waiting, FALSE);
287
288   // return TRUE if we actually hit 0
289   return (queue->items == 0);
290}
291
292
293//============================================================
294//  osd_work_queue_free
295//============================================================
296
297void osd_work_queue_free(osd_work_queue *queue)
298{
299   // if we have threads, clean them up
300   if (queue->thread != NULL)
301   {
302      int threadnum;
303
304      // stop the timer for "waittime" on the main thread
305      end_timing(queue->thread[queue->threads].waittime);
306
307      // signal all the threads to exit
308      atomic_exchange32(&queue->exiting, TRUE);
309      for (threadnum = 0; threadnum < queue->threads; threadnum++)
310      {
311         work_thread_info *thread = &queue->thread[threadnum];
312         if (thread->wakeevent != NULL)
313            osd_event_set(thread->wakeevent);
314      }
315
316      // wait for all the threads to go away
317      for (threadnum = 0; threadnum < queue->threads; threadnum++)
318      {
319         work_thread_info *thread = &queue->thread[threadnum];
320
321         // block on the thread going away, then close the handle
322         if (thread->handle != NULL)
323         {
324            osd_thread_wait_free(thread->handle);
325         }
326
327         // clean up the wake event
328         if (thread->wakeevent != NULL)
329            osd_event_free(thread->wakeevent);
330      }
331
332#if KEEP_STATISTICS
333      // output per-thread statistics
334      for (threadnum = 0; threadnum <= queue->threads; threadnum++)
335      {
336         work_thread_info *thread = &queue->thread[threadnum];
337         osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
338         printf("Thread %d:  items=%9d run=%5.2f%% (%5.2f%%)  spin=%5.2f%%  wait/other=%5.2f%% total=%9d\n",
339               threadnum, thread->itemsdone,
340               (double)thread->runtime * 100.0 / (double)total,
341               (double)thread->actruntime * 100.0 / (double)total,
342               (double)thread->spintime * 100.0 / (double)total,
343               (double)thread->waittime * 100.0 / (double)total,
344               (UINT32) total);
345      }
346#endif
347   }
348
349   // free the list
350   if (queue->thread != NULL)
351      osd_free(queue->thread);
352
353   // free all the events
354   if (queue->doneevent != NULL)
355      osd_event_free(queue->doneevent);
356
357   // free all items in the free list
358   while (queue->free != NULL)
359   {
360      osd_work_item *item = (osd_work_item *)queue->free;
361      queue->free = item->next;
362      if (item->event != NULL)
363         osd_event_free(item->event);
364      osd_free(item);
365   }
366
367   // free all items in the active list
368   while (queue->list != NULL)
369   {
370      osd_work_item *item = (osd_work_item *)queue->list;
371      queue->list = item->next;
372      if (item->event != NULL)
373         osd_event_free(item->event);
374      osd_free(item);
375   }
376
377#if KEEP_STATISTICS
378   printf("Items queued   = %9d\n", queue->itemsqueued);
379   printf("SetEvent calls = %9d\n", queue->setevents);
380   printf("Extra items    = %9d\n", queue->extraitems);
381   printf("Spin loops     = %9d\n", queue->spinloops);
382#endif
383
384   osd_scalable_lock_free(queue->lock);
385   // free the queue itself
386   osd_free(queue);
387}
388
389
390//============================================================
391//  osd_work_item_queue_multiple
392//============================================================
393
394osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
395{
396   osd_work_item *itemlist = NULL, *lastitem = NULL;
397   osd_work_item **item_tailptr = &itemlist;
398   INT32 lockslot;
399   int itemnum;
400
401   // loop over items, building up a local list of work
402   for (itemnum = 0; itemnum < numitems; itemnum++)
403   {
404      osd_work_item *item;
405
406      // first allocate a new work item; try the free list first
407      INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
408      do
409      {
410         item = (osd_work_item *)queue->free;
411      } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
412      osd_scalable_lock_release(queue->lock, lockslot);
413
414      // if nothing, allocate something new
415      if (item == NULL)
416      {
417         // allocate the item
418         item = (osd_work_item *)osd_malloc(sizeof(*item));
419         if (item == NULL)
420            return NULL;
421         item->event = NULL;
422         item->queue = queue;
423         item->done = FALSE;
424      }
425      else
426      {
427         atomic_exchange32(&item->done, FALSE); // needs to be set this way to prevent data race/usage of uninitialized memory on Linux
428      }
429
430      // fill in the basics
431      item->next = NULL;
432      item->callback = callback;
433      item->param = parambase;
434      item->result = NULL;
435      item->flags = flags;
436
437      // advance to the next
438      lastitem = item;
439      *item_tailptr = item;
440      item_tailptr = &item->next;
441      parambase = (UINT8 *)parambase + paramstep;
442   }
443
444   // enqueue the whole thing within the critical section
445   lockslot = osd_scalable_lock_acquire(queue->lock);
446   *queue->tailptr = itemlist;
447   queue->tailptr = item_tailptr;
448   osd_scalable_lock_release(queue->lock, lockslot);
449
450   // increment the number of items in the queue
451   atomic_add32(&queue->items, numitems);
452   add_to_stat(&queue->itemsqueued, numitems);
453
454   // look for free threads to do the work
455   if (queue->livethreads < queue->threads)
456   {
457      int threadnum;
458
459      // iterate over all the threads
460      for (threadnum = 0; threadnum < queue->threads; threadnum++)
461      {
462         work_thread_info *thread = &queue->thread[threadnum];
463
464         // if this thread is not active, wake him up
465         if (!thread->active)
466         {
467            osd_event_set(thread->wakeevent);
468            add_to_stat(&queue->setevents, 1);
469
470            // for non-shared, the first one we find is good enough
471            if (--numitems == 0)
472               break;
473         }
474      }
475   }
476
477   // if no threads, run the queue now on this thread
478   if (queue->threads == 0)
479   {
480      end_timing(queue->thread[0].waittime);
481      worker_thread_process(queue, &queue->thread[0]);
482      begin_timing(queue->thread[0].waittime);
483   }
484   // only return the item if it won't get released automatically
485   return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
486}
487
488
489//============================================================
490//  osd_work_item_wait
491//============================================================
492
493int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
494{
495   // if we're done already, just return
496   if (item->done)
497      return TRUE;
498
499   // if we don't have an event, create one
500   if (item->event == NULL)
501   {
502      INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
503      item->event = osd_event_alloc(TRUE, FALSE);     // manual reset, not signalled
504      osd_scalable_lock_release(item->queue->lock, lockslot);
505   }
506   else
507      osd_event_reset(item->event);
508
509   // if we don't have an event, we need to spin (shouldn't ever really happen)
510   if (item->event == NULL)
511   {
512      osd_ticks_t stopspin = osd_ticks() + timeout;
513      do {
514         int spin = 10000;
515         while (--spin && !item->done)
516            osd_yield_processor();
517      } while (!item->done && osd_ticks() < stopspin);
518   }
519
520   // otherwise, block on the event until done
521   else if (!item->done)
522      osd_event_wait(item->event, timeout);
523
524   // return TRUE if the refcount actually hit 0
525   return item->done;
526}
527
528
529//============================================================
530//  osd_work_item_result
531//============================================================
532
533void *osd_work_item_result(osd_work_item *item)
534{
535   return item->result;
536}
537
538
539//============================================================
540//  osd_work_item_release
541//============================================================
542
543void osd_work_item_release(osd_work_item *item)
544{
545   osd_work_item *next;
546
547   // make sure we're done first
548   osd_work_item_wait(item, 100 * osd_ticks_per_second());
549
550   // add us to the free list on our queue
551   INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
552   do
553   {
554      next = (osd_work_item *)item->queue->free;
555      item->next = next;
556   } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
557   osd_scalable_lock_release(item->queue->lock, lockslot);
558}
559
560
561//============================================================
562//  effective_num_processors
563//============================================================
564
565static int effective_num_processors(void)
566{
567   int physprocs = osd_get_num_processors();
568
569   // osd_num_processors == 0 for 'auto'
570   if (osd_num_processors > 0)
571   {
572      return MIN(4 * physprocs, osd_num_processors);
573   }
574   else
575   {
576      char *procsoverride;
577      int numprocs = 0;
578
579      // if the OSDPROCESSORS environment variable is set, use that value if valid
580      // note that we permit more than the real number of processors for testing
581      procsoverride = osd_getenv(ENV_PROCESSORS);
582      if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0)
583         return MIN(4 * physprocs, numprocs);
584
585      // otherwise, return the info from the system
586      return physprocs;
587   }
588}
589
590
591//============================================================
592//  worker_thread_entry
593//============================================================
594
595static void *worker_thread_entry(void *param)
596{
597   work_thread_info *thread = (work_thread_info *)param;
598   osd_work_queue *queue = thread->queue;
599
600   #if defined(SDLMAME_MACOSX)
601   void *arp = NewAutoreleasePool();
602   #endif
603
604   // loop until we exit
605   for ( ;; )
606   {
607      // block waiting for work or exit
608      // bail on exit, and only wait if there are no pending items in queue
609      if (queue->exiting)
610         break;
611
612      if (!queue_has_list_items(queue))
613      {
614         begin_timing(thread->waittime);
615         osd_event_wait(thread->wakeevent, INFINITE);
616         end_timing(thread->waittime);
617      }
618
619      if (queue->exiting)
620         break;
621
622      // indicate that we are live
623      atomic_exchange32(&thread->active, TRUE);
624      atomic_increment32(&queue->livethreads);
625
626      // process work items
627      for ( ;; )
628      {
629         osd_ticks_t stopspin;
630
631         // process as much as we can
632         worker_thread_process(queue, thread);
633
634         // if we're a high frequency queue, spin for a while before giving up
635         if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->list == NULL)
636         {
637            // spin for a while looking for more work
638            begin_timing(thread->spintime);
639            stopspin = osd_ticks() + SPIN_LOOP_TIME;
640
641            do {
642               int spin = 10000;
643               while (--spin && queue->list == NULL)
644                  osd_yield_processor();
645            } while (queue->list == NULL && osd_ticks() < stopspin);
646            end_timing(thread->spintime);
647         }
648
649         // if nothing more, release the processor
650         if (!queue_has_list_items(queue))
651            break;
652         add_to_stat(&queue->spinloops, 1);
653      }
654
655      // decrement the live thread count
656      atomic_exchange32(&thread->active, FALSE);
657      atomic_decrement32(&queue->livethreads);
658   }
659
660   #if defined(SDLMAME_MACOSX)
661   ReleaseAutoreleasePool(arp);
662   #endif
663
664   return NULL;
665}
666
667
668//============================================================
669//  worker_thread_process
670//============================================================
671
672static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
673{
674   int threadid = thread - queue->thread;
675
676   begin_timing(thread->runtime);
677
678   // loop until everything is processed
679   while (true)
680   {
681      osd_work_item *item = NULL;
682
683      bool end_loop = false;
684
685      // use a critical section to synchronize the removal of items
686      {
687         INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
688         if (queue->list == NULL)
689         {
690            end_loop = true;
691         }
692         else
693         {
694            // pull the item from the queue
695            item = (osd_work_item *)queue->list;
696            if (item != NULL)
697            {
698               queue->list = item->next;
699               if (queue->list == NULL)
700                  queue->tailptr = (osd_work_item **)&queue->list;
701            }
702         }
703         osd_scalable_lock_release(queue->lock, lockslot);
704      }
705
706      if (end_loop)
707         break;
708
709      // process non-NULL items
710      if (item != NULL)
711      {
712         // call the callback and stash the result
713         begin_timing(thread->actruntime);
714         item->result = (*item->callback)(item->param, threadid);
715         end_timing(thread->actruntime);
716
717         // decrement the item count after we are done
718         atomic_decrement32(&queue->items);
719         atomic_exchange32(&item->done, TRUE);
720         add_to_stat(&thread->itemsdone, 1);
721
722         // if it's an auto-release item, release it
723         if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
724            osd_work_item_release(item);
725
726         // set the result and signal the event
727         else
728         {
729            INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
730            if (item->event != NULL)
731            {
732               osd_event_set(item->event);
733               add_to_stat(&item->queue->setevents, 1);
734            }
735            osd_scalable_lock_release(item->queue->lock, lockslot);
736         }
737
738         // if we removed an item and there's still work to do, bump the stats
739         if (queue_has_list_items(queue))
740            add_to_stat(&queue->extraitems, 1);
741      }
742   }
743
744   // we don't need to set the doneevent for multi queues because they spin
745   if (queue->waiting)
746   {
747      osd_event_set(queue->doneevent);
748      add_to_stat(&queue->setevents, 1);
749   }
750
751   end_timing(thread->runtime);
752}
753
754bool queue_has_list_items(osd_work_queue *queue)
755{
756   INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
757   bool has_list_items = (queue->list != NULL);
758   osd_scalable_lock_release(queue->lock, lockslot);
759   return has_list_items;
760}
761#endif // SDLMAME_NOASM
trunk/src/osd/windows/windows.mak
r242715r242716
349349   $(WINOBJ)/winutil.o \
350350   $(WINOBJ)/winclip.o \
351351   $(WINOBJ)/winsocket.o \
352   $(OSDOBJ)/modules/sync/work_osd.o \
352   $(WINOBJ)/winwork.o \
353353   $(WINOBJ)/winptty.o \
354354   $(WINOBJ)/winos.o \
355355
trunk/src/osd/windows/winwork.c
r0r242716
1// license:BSD-3-Clause
2// copyright-holders:Aaron Giles
3//============================================================
4//
5//  winwork.c - Win32 OSD core work item functions
6//
7//============================================================
8
9// standard windows headers
10#define WIN32_LEAN_AND_MEAN
11#include <windows.h>
12#include <process.h>
13#include <tchar.h>
14#include <stdlib.h>
15
16#ifdef __GNUC__
17#include <stdint.h>
18#endif
19
20// MAME headers
21#include "osdcore.h"
22
23#include "modules/sync/osdsync.h"
24#include "winos.h"
25
26#include "eminline.h"
27
28
29//============================================================
30//  DEBUGGING
31//============================================================
32
33#define KEEP_STATISTICS         (0)
34
35//============================================================
36//  PARAMETERS
37//============================================================
38
39#define ENV_PROCESSORS               "OSDPROCESSORS"
40#define ENV_WORKQUEUEMAXTHREADS      "OSDWORKQUEUEMAXTHREADS"
41
42#define SPIN_LOOP_TIME          (osd_ticks_per_second() / 1000)
43
44
45
46//============================================================
47//  MACROS
48//============================================================
49
50#if KEEP_STATISTICS
51#define add_to_stat(v,x)        do { atomic_add32((v), (x)); } while (0)
52#define begin_timing(v)         do { (v) -= get_profile_ticks(); } while (0)
53#define end_timing(v)           do { (v) += get_profile_ticks(); } while (0)
54#else
55#define add_to_stat(v,x)        do { } while (0)
56#define begin_timing(v)         do { } while (0)
57#define end_timing(v)           do { } while (0)
58#endif
59
60#if __GNUC__ && defined(__i386__) && !defined(__x86_64)
61#undef YieldProcessor
62#endif
63
64#ifndef YieldProcessor
65#ifdef __GNUC__
66INLINE void osd_yield_processor(void)
67{
68   __asm__ __volatile__ ( "rep; nop" );
69}
70#else
71INLINE void osd_yield_processor(void)
72{
73   __asm { rep nop }
74}
75#endif
76#else
77#define osd_yield_processor YieldProcessor
78#endif
79
80
81
82//============================================================
83//  TYPE DEFINITIONS
84//============================================================
85
86struct work_thread_info
87{
88   osd_work_queue *    queue;          // pointer back to the queue
89   osd_thread *        handle;         // handle to the thread
90   osd_event *         wakeevent;      // wake event for the thread
91   volatile INT32      active;         // are we actively processing work?
92
93#if KEEP_STATISTICS
94   INT32               itemsdone;
95   osd_ticks_t         actruntime;
96   osd_ticks_t         runtime;
97   osd_ticks_t         spintime;
98   osd_ticks_t         waittime;
99#endif
100};
101
102
103struct osd_work_queue
104{
105   osd_scalable_lock * lock;           // lock for protecting the queue
106   osd_work_item * volatile list;      // list of items in the queue
107   osd_work_item ** volatile tailptr;  // pointer to the tail pointer of work items in the queue
108   osd_work_item * volatile free;      // free list of work items
109   volatile INT32      items;          // items in the queue
110   volatile INT32      livethreads;    // number of live threads
111   volatile INT32      waiting;        // is someone waiting on the queue to complete?
112   volatile INT32      exiting;        // should the threads exit on their next opportunity?
113   UINT32              threads;        // number of threads in this queue
114   UINT32              flags;          // creation flags
115   work_thread_info *  thread;         // array of thread information
116   osd_event   *       doneevent;      // event signalled when work is complete
117
118#if KEEP_STATISTICS
119   volatile INT32      itemsqueued;    // total items queued
120   volatile INT32      setevents;      // number of times we called SetEvent
121   volatile INT32      extraitems;     // how many extra items we got after the first in the queue loop
122   volatile INT32      spinloops;      // how many times spinning bought us more items
123#endif
124};
125
126
127struct osd_work_item
128{
129   osd_work_item *     next;           // pointer to next item
130   osd_work_queue *    queue;          // pointer back to the owning queue
131   osd_work_callback   callback;       // callback function
132   void *              param;          // callback parameter
133   void *              result;         // callback result
134   osd_event *         event;          // event signalled when complete
135   UINT32              flags;          // creation flags
136   volatile INT32      done;           // is the item done?
137};
138
139//============================================================
140//  GLOBAL VARIABLES
141//============================================================
142
143int osd_num_processors = 0;
144
145//============================================================
146//  FUNCTION PROTOTYPES
147//============================================================
148
149static int effective_num_processors(void);
150static void * worker_thread_entry(void *param);
151static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
152static bool queue_has_list_items(osd_work_queue *queue);
153
154
155//============================================================
156//  osd_work_queue_alloc
157//============================================================
158
159osd_work_queue *osd_work_queue_alloc(int flags)
160{
161   int numprocs = effective_num_processors();
162   osd_work_queue *queue;
163   int threadnum;
164   char *osdworkqueuemaxthreads = osd_getenv("OSDWORKQUEUEMAXTHREADS");
165
166   // allocate a new queue
167   queue = (osd_work_queue *)osd_malloc(sizeof(*queue));
168   if (queue == NULL)
169      goto error;
170   memset(queue, 0, sizeof(*queue));
171
172   // initialize basic queue members
173   queue->tailptr = (osd_work_item **)&queue->list;
174   queue->flags = flags;
175
176   // allocate events for the queue
177   queue->doneevent = osd_event_alloc(TRUE, TRUE);     // manual reset, signalled
178   if (queue->doneevent == NULL)
179      goto error;
180
181   // initialize the critical section
182   queue->lock = osd_scalable_lock_alloc();
183   if (queue->lock == NULL)
184      goto error;
185
186   // determine how many threads to create...
187   // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
188   if (numprocs == 1)
189      queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
190
191   // on an n-CPU system, create n threads for multi queues, and 1 thread for everything else
192   else
193      queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? numprocs : 1;
194
195   if (osdworkqueuemaxthreads != NULL && sscanf(osdworkqueuemaxthreads, "%d", &threadnum) == 1 && queue->threads > threadnum)
196      queue->threads = threadnum;
197
198   // multi-queues with high frequency items should top out at 4 for now
199   // since we have scaling problems above that
200   if ((flags & WORK_QUEUE_FLAG_HIGH_FREQ) && queue->threads > 1)
201      queue->threads = MIN(queue->threads - 1, 4);
202
203   // clamp to the maximum
204   queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
205
206   // allocate memory for thread array (+1 to count the calling thread)
207   queue->thread = (work_thread_info *)osd_malloc_array((queue->threads + 1) * sizeof(queue->thread[0]));
208   if (queue->thread == NULL)
209      goto error;
210   memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
211
212   // iterate over threads
213   for (threadnum = 0; threadnum < queue->threads; threadnum++)
214   {
215      work_thread_info *thread = &queue->thread[threadnum];
216
217      // set a pointer back to the queue
218      thread->queue = queue;
219
220      // create the per-thread wake event
221      thread->wakeevent = osd_event_alloc(FALSE, FALSE);  // auto-reset, not signalled
222      if (thread->wakeevent == NULL)
223         goto error;
224
225      // create the thread
226      thread->handle = osd_thread_create(worker_thread_entry, thread);
227      if (thread->handle == NULL)
228         goto error;
229
230      // set its priority: I/O threads get high priority because they are assumed to be
231      // blocked most of the time; other threads just match the creator's priority
232      if (flags & WORK_QUEUE_FLAG_IO)
233         osd_thread_adjust_priority(thread->handle, 1);
234      else
235         osd_thread_adjust_priority(thread->handle, 0);
236   }
237
238   // start a timer going for "waittime" on the main thread
239   begin_timing(queue->thread[queue->threads].waittime);
240   return queue;
241
242error:
243   osd_work_queue_free(queue);
244   return NULL;
245}
246
247
248//============================================================
249//  osd_work_queue_items
250//============================================================
251
252int osd_work_queue_items(osd_work_queue *queue)
253{
254   // return the number of items currently in the queue
255   return queue->items;
256}
257
258
259//============================================================
260//  osd_work_queue_wait
261//============================================================
262
263int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
264{
265   // if no threads, no waiting
266   if (queue->threads == 0)
267      return TRUE;
268
269   // if no items, we're done
270   if (queue->items == 0)
271      return TRUE;
272
273   // if this is a multi queue, help out rather than doing nothing
274   if (queue->flags & WORK_QUEUE_FLAG_MULTI)
275   {
276      work_thread_info *thread = &queue->thread[queue->threads];
277
278      end_timing(thread->waittime);
279
280      // process what we can as a worker thread
281      worker_thread_process(queue, thread);
282
283      // if we're a high frequency queue, spin until done
284      if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->items != 0)
285      {
286         osd_ticks_t stopspin = osd_ticks() + timeout;
287
288         // spin until we're done
289         begin_timing(thread->spintime);
290         while (queue->items != 0 && osd_ticks() < stopspin)
291            osd_yield_processor();
292         end_timing(thread->spintime);
293
294         begin_timing(thread->waittime);
295         return (queue->items == 0);
296      }
297      begin_timing(thread->waittime);
298   }
299
300   // reset our done event and double-check the items before waiting
301   osd_event_reset(queue->doneevent);
302   atomic_exchange32(&queue->waiting, TRUE);
303   if (queue->items != 0)
304      osd_event_wait(queue->doneevent, timeout);
305   atomic_exchange32(&queue->waiting, FALSE);
306
307   // return TRUE if we actually hit 0
308   return (queue->items == 0);
309}
310
311
312//============================================================
313//  osd_work_queue_free
314//============================================================
315
316void osd_work_queue_free(osd_work_queue *queue)
317{
318   // if we have threads, clean them up
319   if (queue->thread != NULL)
320   {
321      int threadnum;
322
323      // stop the timer for "waittime" on the main thread
324      end_timing(queue->thread[queue->threads].waittime);
325
326      // signal all the threads to exit
327      atomic_exchange32(&queue->exiting, TRUE);
328      for (threadnum = 0; threadnum < queue->threads; threadnum++)
329      {
330         work_thread_info *thread = &queue->thread[threadnum];
331         if (thread->wakeevent != NULL)
332            osd_event_set(thread->wakeevent);
333      }
334
335      // wait for all the threads to go away
336      for (threadnum = 0; threadnum < queue->threads; threadnum++)
337      {
338         work_thread_info *thread = &queue->thread[threadnum];
339
340         // block on the thread going away, then close the handle
341         if (thread->handle != NULL)
342         {
343            osd_thread_wait_free(thread->handle);
344         }
345
346         // clean up the wake event
347         if (thread->wakeevent != NULL)
348            osd_event_free(thread->wakeevent);
349      }
350
351#if KEEP_STATISTICS
352      // output per-thread statistics
353      for (threadnum = 0; threadnum <= queue->threads; threadnum++)
354      {
355         work_thread_info *thread = &queue->thread[threadnum];
356         osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
357         printf("Thread %d:  items=%9d run=%5.2f%% (%5.2f%%)  spin=%5.2f%%  wait/other=%5.2f%% total=%9d\n",
358               threadnum, thread->itemsdone,
359               (double)thread->runtime * 100.0 / (double)total,
360               (double)thread->actruntime * 100.0 / (double)total,
361               (double)thread->spintime * 100.0 / (double)total,
362               (double)thread->waittime * 100.0 / (double)total,
363               (UINT32) total);
364      }
365#endif
366   }
367
368   // free the list
369   if (queue->thread != NULL)
370      osd_free(queue->thread);
371
372   // free all the events
373   if (queue->doneevent != NULL)
374      osd_event_free(queue->doneevent);
375
376   // free all items in the free list
377   while (queue->free != NULL)
378   {
379      osd_work_item *item = (osd_work_item *)queue->free;
380      queue->free = item->next;
381      if (item->event != NULL)
382         osd_event_free(item->event);
383      osd_free(item);
384   }
385
386   // free all items in the active list
387   while (queue->list != NULL)
388   {
389      osd_work_item *item = (osd_work_item *)queue->list;
390      queue->list = item->next;
391      if (item->event != NULL)
392         osd_event_free(item->event);
393      osd_free(item);
394   }
395
396#if KEEP_STATISTICS
397   printf("Items queued   = %9d\n", queue->itemsqueued);
398   printf("SetEvent calls = %9d\n", queue->setevents);
399   printf("Extra items    = %9d\n", queue->extraitems);
400   printf("Spin loops     = %9d\n", queue->spinloops);
401#endif
402
403   osd_scalable_lock_free(queue->lock);
404   // free the queue itself
405   osd_free(queue);
406}
407
408
409//============================================================
410//  osd_work_item_queue_multiple
411//============================================================
412
413osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
414{
415   osd_work_item *itemlist = NULL, *lastitem = NULL;
416   osd_work_item **item_tailptr = &itemlist;
417   INT32 lockslot;
418   int itemnum;
419
420   // loop over items, building up a local list of work
421   for (itemnum = 0; itemnum < numitems; itemnum++)
422   {
423      osd_work_item *item;
424
425      // first allocate a new work item; try the free list first
426      INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
427      do
428      {
429         item = (osd_work_item *)queue->free;
430      } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
431      osd_scalable_lock_release(queue->lock, lockslot);
432
433      // if nothing, allocate something new
434      if (item == NULL)
435      {
436         // allocate the item
437         item = (osd_work_item *)osd_malloc(sizeof(*item));
438         if (item == NULL)
439            return NULL;
440         item->event = NULL;
441         item->queue = queue;
442         item->done = FALSE;
443      }
444      else
445      {
446         atomic_exchange32(&item->done, FALSE); // needs to be set this way to prevent data race/usage of uninitialized memory on Linux
447      }
448
449      // fill in the basics
450      item->next = NULL;
451      item->callback = callback;
452      item->param = parambase;
453      item->result = NULL;
454      item->flags = flags;
455
456      // advance to the next
457      lastitem = item;
458      *item_tailptr = item;
459      item_tailptr = &item->next;
460      parambase = (UINT8 *)parambase + paramstep;
461   }
462
463   // enqueue the whole thing within the critical section
464   lockslot = osd_scalable_lock_acquire(queue->lock);
465   *queue->tailptr = itemlist;
466   queue->tailptr = item_tailptr;
467   osd_scalable_lock_release(queue->lock, lockslot);
468
469   // increment the number of items in the queue
470   atomic_add32(&queue->items, numitems);
471   add_to_stat(&queue->itemsqueued, numitems);
472
473   // look for free threads to do the work
474   if (queue->livethreads < queue->threads)
475   {
476      int threadnum;
477
478      // iterate over all the threads
479      for (threadnum = 0; threadnum < queue->threads; threadnum++)
480      {
481         work_thread_info *thread = &queue->thread[threadnum];
482
483         // if this thread is not active, wake him up
484         if (!thread->active)
485         {
486            osd_event_set(thread->wakeevent);
487            add_to_stat(&queue->setevents, 1);
488
489            // for non-shared, the first one we find is good enough
490            if (--numitems == 0)
491               break;
492         }
493      }
494   }
495
496   // if no threads, run the queue now on this thread
497   if (queue->threads == 0)
498      worker_thread_process(queue, &queue->thread[0]);
499
500   // only return the item if it won't get released automatically
501   return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
502}
503
504
505//============================================================
506//  osd_work_item_wait
507//============================================================
508
509int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
510{
511   // if we're done already, just return
512   if (item->done)
513      return TRUE;
514
515   // if we don't have an event, create one
516   if (item->event == NULL)
517   {
518      INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
519      item->event = osd_event_alloc(TRUE, FALSE);     // manual reset, not signalled
520      osd_scalable_lock_release(item->queue->lock, lockslot);
521   }
522   else
523      osd_event_reset(item->event);
524
525   // if we don't have an event, we need to spin (shouldn't ever really happen)
526   if (item->event == NULL)
527   {
528      osd_ticks_t stopspin = osd_ticks() + timeout;
529      while (!item->done && osd_ticks() < stopspin)
530         osd_yield_processor();
531   }
532
533   // otherwise, block on the event until done
534   else if (!item->done)
535      osd_event_wait(item->event, timeout);
536
537   // return TRUE if the refcount actually hit 0
538   return item->done;
539}
540
541
542//============================================================
543//  osd_work_item_result
544//============================================================
545
546void *osd_work_item_result(osd_work_item *item)
547{
548   return item->result;
549}
550
551
552//============================================================
553//  osd_work_item_release
554//============================================================
555
556void osd_work_item_release(osd_work_item *item)
557{
558   osd_work_item *next;
559
560   // make sure we're done first
561   osd_work_item_wait(item, 100 * osd_ticks_per_second());
562
563   // add us to the free list on our queue
564   INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
565   do
566   {
567      next = (osd_work_item *)item->queue->free;
568      item->next = next;
569   } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
570   osd_scalable_lock_release(item->queue->lock, lockslot);
571}
572
573
574//============================================================
575//  effective_num_processors
576//============================================================
577
578static int effective_num_processors(void)
579{
580   int physprocs = osd_get_num_processors();
581
582   // osd_num_processors == 0 for 'auto'
583   if (osd_num_processors > 0)
584   {
585      return MIN(4 * physprocs, osd_num_processors);
586   }
587   else
588   {
589      char *procsoverride;
590      int numprocs = 0;
591
592      // if the OSDPROCESSORS environment variable is set, use that value if valid
593      // note that we permit more than the real number of processors for testing
594      procsoverride = osd_getenv(ENV_PROCESSORS);
595      if (procsoverride != NULL && sscanf(procsoverride, "%d", &numprocs) == 1 && numprocs > 0)
596         return MIN(4 * physprocs, numprocs);
597
598      // otherwise, return the info from the system
599      return physprocs;
600   }
601}
602
603
604//============================================================
605//  worker_thread_entry
606//============================================================
607
608static void *worker_thread_entry(void *param)
609{
610   work_thread_info *thread = (work_thread_info *)param;
611   osd_work_queue *queue = thread->queue;
612
613   // loop until we exit
614   for ( ;; )
615   {
616      // block waiting for work or exit
617      // bail on exit, and only wait if there are no pending items in queue
618      if (queue->exiting)
619         break;
620
621      if (!queue_has_list_items(queue))
622      {
623         begin_timing(thread->waittime);
624         osd_event_wait(thread->wakeevent, INFINITE);
625         end_timing(thread->waittime);
626      }
627
628      if (queue->exiting)
629         break;
630
631      // indicate that we are live
632      atomic_exchange32(&thread->active, TRUE);
633      atomic_increment32(&queue->livethreads);
634
635      // process work items
636      for ( ;; )
637      {
638         osd_ticks_t stopspin;
639
640         // process as much as we can
641         worker_thread_process(queue, thread);
642
643         // if we're a high frequency queue, spin for a while before giving up
644         if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ && queue->list == NULL)
645         {
646            // spin for a while looking for more work
647            begin_timing(thread->spintime);
648            stopspin = osd_ticks() + SPIN_LOOP_TIME;
649            while (queue->list == NULL && osd_ticks() < stopspin)
650               osd_yield_processor();
651            end_timing(thread->spintime);
652         }
653
654         // if nothing more, release the processor
655         if (!queue_has_list_items(queue))
656            break;
657         add_to_stat(&queue->spinloops, 1);
658      }
659
660      // decrement the live thread count
661      atomic_exchange32(&thread->active, FALSE);
662      atomic_decrement32(&queue->livethreads);
663   }
664   return NULL;
665}
666
667
668//============================================================
669//  worker_thread_process
670//============================================================
671
672static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
673{
674   int threadid = thread - queue->thread;
675
676   begin_timing(thread->runtime);
677
678   // loop until everything is processed
679   while (true)
680   {
681      osd_work_item *item = NULL;
682
683      bool end_loop = false;
684
685      // use a critical section to synchronize the removal of items
686      {
687         INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
688         if (queue->list == NULL)
689         {
690            end_loop = true;
691         }
692         else
693         {
694            // pull the item from the queue
695            item = (osd_work_item *)queue->list;
696            if (item != NULL)
697            {
698               queue->list = item->next;
699               if (queue->list == NULL)
700                  queue->tailptr = (osd_work_item **)&queue->list;
701            }
702         }
703         osd_scalable_lock_release(queue->lock, lockslot);
704      }
705
706      if (end_loop)
707         break;
708
709      // process non-NULL items
710      if (item != NULL)
711      {
712         // call the callback and stash the result
713         begin_timing(thread->actruntime);
714         item->result = (*item->callback)(item->param, threadid);
715         end_timing(thread->actruntime);
716
717         // decrement the item count after we are done
718         atomic_decrement32(&queue->items);
719         atomic_exchange32(&item->done, TRUE);
720         add_to_stat(&thread->itemsdone, 1);
721
722         // if it's an auto-release item, release it
723         if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
724            osd_work_item_release(item);
725
726         // set the result and signal the event
727         else
728         {
729            INT32 lockslot = osd_scalable_lock_acquire(item->queue->lock);
730            if (item->event != NULL)
731            {
732               osd_event_set(item->event);
733               add_to_stat(&item->queue->setevents, 1);
734            }
735            osd_scalable_lock_release(item->queue->lock, lockslot);
736         }
737
738         // if we removed an item and there's still work to do, bump the stats
739         if (queue_has_list_items(queue))
740            add_to_stat(&queue->extraitems, 1);
741      }
742   }
743
744   // we don't need to set the doneevent for multi queues because they spin
745   if (queue->waiting)
746   {
747      osd_event_set(queue->doneevent);
748      add_to_stat(&queue->setevents, 1);
749   }
750
751   end_timing(thread->runtime);
752}
753
754bool queue_has_list_items(osd_work_queue *queue)
755{
756   INT32 lockslot = osd_scalable_lock_acquire(queue->lock);
757   bool has_list_items = (queue->list != NULL);
758   osd_scalable_lock_release(queue->lock, lockslot);
759   return has_list_items;
760}


Previous 199869 Revisions Next


© 1997-2024 The MAME Team