Annihigate flash thread.

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3750 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Meep-Eep
2012-01-27 23:29:03 +00:00
parent 20e2ea337f
commit d8d201ee54
23 changed files with 359 additions and 96 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
Changes towards version 0.8:
- Switch SetFlashRect() to the new flash code -- SvdB
- Annihigate flash thread - SvdB
- Switch SetFlashRect() to the new flash code - SvdB
- Add C++ support to the build system, from Scott A. Colcord
- Cleaning up DoModifyShips() - SvdB
- Added Valgrind suppression file, from Louis Delacroix
+12
View File
@@ -215,6 +215,14 @@ SOURCE=..\..\src\libs\callback\alarm.h
# End Source File
# Begin Source File
SOURCE=..\..\src\libs\callback\async.c
# End Source File
# Begin Source File
SOURCE=..\..\src\libs\callback\async.h
# End Source File
# Begin Source File
SOURCE=..\..\src\libs\callback\callback.c
# End Source File
# Begin Source File
@@ -1470,6 +1478,10 @@ SOURCE=..\..\src\libs\alarm.h
# End Source File
# Begin Source File
SOURCE=..\..\src\libs\async.h
# End Source File
# Begin Source File
SOURCE=..\..\src\libs\callback.h
# End Source File
# Begin Source File
+4 -4
View File
@@ -12,8 +12,8 @@ fi
# uqm_SUBDIRS="$UQM_SUBDIRS debug"
#fi
uqm_HFILES="alarm.h callback.h cdplib.h compiler.h declib.h file.h gfxlib.h
heap.h inplib.h list.h log.h mathlib.h md5.h memlib.h misc.h net.h
platform.h reslib.h sndlib.h strlib.h tasklib.h threadlib.h
timelib.h uio.h uioutils.h unicode.h vidlib.h"
uqm_HFILES="alarm.h async.h callback.h cdplib.h compiler.h declib.h file.h
gfxlib.h heap.h inplib.h list.h log.h mathlib.h md5.h memlib.h
misc.h net.h platform.h reslib.h sndlib.h strlib.h tasklib.h
threadlib.h timelib.h uio.h uioutils.h unicode.h vidlib.h"
+10
View File
@@ -0,0 +1,10 @@
#if defined(__cplusplus)
extern "C" {
#endif
#include "callback/async.h"
#if defined(__cplusplus)
}
#endif
+2 -2
View File
@@ -1,2 +1,2 @@
uqm_CFILES="alarm.c callback.c"
uqm_HFILES="alarm.h callback.h"
uqm_CFILES="alarm.c async.c callback.c"
uqm_HFILES="alarm.h async.h callback.h"
+50 -7
View File
@@ -18,6 +18,7 @@
#include "alarm.h"
#include SDL_INCLUDE(SDL.h)
#include "libs/heap.h"
#include <assert.h>
@@ -71,19 +72,36 @@ Alarm_uninit(void) {
}
static inline AlarmTime
AlarmTime_nowMS(void) {
AlarmTime_nowMs(void) {
return SDL_GetTicks();
}
Alarm *
Alarm_addRelativeMs(Uint32 ms, AlarmCallback callback,
Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback,
AlarmCallbackArg arg) {
Alarm *alarm;
assert(alarmHeap != NULL);
alarm = Alarm_alloc();
alarm->time = AlarmTime_nowMS() + ms;
alarm->time = ms;
alarm->callback = callback;
alarm->arg = arg;
Heap_add(alarmHeap, (HeapValue *) alarm);
return alarm;
}
Alarm *
Alarm_addRelativeMs(uint32 ms, AlarmCallback callback,
AlarmCallbackArg arg) {
Alarm *alarm;
assert(alarmHeap != NULL);
alarm = Alarm_alloc();
alarm->time = AlarmTime_nowMs() + ms;
alarm->callback = callback;
alarm->arg = arg;
@@ -99,15 +117,40 @@ Alarm_remove(Alarm *alarm) {
Alarm_free(alarm);
}
// Process at most one alarm, if its time has come.
// It is safe to call this function again from inside a callback function
// that it called. It should not be called from multiple threads at once.
bool
Alarm_processOne(void)
{
AlarmTime now;
Alarm *alarm;
assert(alarmHeap != NULL);
if (!Heap_hasMore(alarmHeap))
return false;
now = AlarmTime_nowMs();
alarm = (Alarm *) Heap_first(alarmHeap);
if (now < alarm->time)
return false;
Heap_pop(alarmHeap);
alarm->callback(alarm->arg);
Alarm_free(alarm);
return true;
}
#if 0
// It is safe to call this function again from inside a callback function
// that it called. It should not be called from multiple threads at once.
void
Alarm_process(void) {
Alarm_processAll(void) {
AlarmTime now;
assert(alarmHeap != NULL);
now = AlarmTime_nowMS();
now = AlarmTime_nowMs();
while (Heap_hasMore(alarmHeap)) {
Alarm *alarm = (Alarm *) Heap_first(alarmHeap);
@@ -119,8 +162,9 @@ Alarm_process(void) {
Alarm_free(alarm);
}
}
#endif
Uint32
uint32
Alarm_timeBeforeNextMs(void) {
Alarm *alarm;
@@ -131,4 +175,3 @@ Alarm_timeBeforeNextMs(void) {
return alarmTimeToMsUint32(alarm->time);
}
+9 -7
View File
@@ -22,11 +22,10 @@
#include "port.h"
#include "types.h"
#include SDL_INCLUDE(SDL.h)
typedef Uint32 AlarmTime;
static inline Uint32
typedef uint32 AlarmTime;
static inline uint32
alarmTimeToMsUint32(AlarmTime time) {
return (Uint32) time;
return (uint32) time;
}
typedef struct Alarm Alarm;
@@ -44,11 +43,14 @@ struct Alarm {
void Alarm_init(void);
void Alarm_uninit(void);
Alarm *Alarm_addRelativeMs(Uint32 ms, AlarmCallback callback,
Alarm *Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback,
AlarmCallbackArg arg);
Alarm *Alarm_addRelativeMs(uint32 ms, AlarmCallback callback,
AlarmCallbackArg arg);
void Alarm_remove(Alarm *alarm);
void Alarm_process(void);
Uint32 Alarm_timeBeforeNextMs(void);
bool Alarm_processOne(void);
void Alarm_processAll(void);
uint32 Alarm_timeBeforeNextMs(void);
#endif /* _ALARM_H */
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2012 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "async.h"
#include "libs/alarm.h"
#include "libs/callback.h"
// Process all alarms and callbacks.
// First, all scheduled callbacks are called.
// Then each alarm due is called, and after each of these alarms, the
// callbacks scheduled by this alarm are called.
void
Async_process(void)
{
// Call pending callbacks.
Callback_process();
for (;;) {
if (!Alarm_processOne())
return;
// Call callbacks scheduled from the last alarm.
Callback_process();
}
}
// Returns the next time that some asynchronous callback is
// to be called. Note that all values lower than the current time
// should be considered as 'somewhere in the past'.
uint32
Async_timeBeforeNextMs(void) {
if (Callback_haveMore()) {
// Any time before the current time is ok, though we reserve 0 so
// that the caller may use it as a special value in its own code.
return 1;
}
return Alarm_timeBeforeNextMs();
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2012 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _ASYNC_H
#define _ASYNC_H
#include "types.h"
void Async_process(void);
uint32 Async_timeBeforeNextMs(void);
#endif /* _ASYNC_H */
+10
View File
@@ -170,4 +170,14 @@ Callback_process(void) {
}
}
bool
Callback_haveMore(void) {
bool result;
CallbackList_lock();
result = (callbacks != NULL);
CallbackList_unlock();
return result;
}
+1
View File
@@ -36,6 +36,7 @@ void Callback_init(void);
CallbackID Callback_add(CallbackFunction callback, CallbackArg arg);
bool Callback_remove(CallbackID id);
void Callback_process(void);
bool Callback_haveMore(void);
#endif /* _CALLBACK_H */
@@ -211,7 +211,7 @@ PlaybackTaskFunc (void *data)
mixer_MixFake (NULL, stream, len);
delay = period - (GetTimeCounter () - entryTime);
if (delay > 0)
SleepThread (delay);
HibernateThread (delay);
}
HFree (stream);
+1 -1
View File
@@ -568,7 +568,7 @@ StreamDecoderTaskFunc (void *data)
if (active_streams == 0)
{ // Throttle down the thread when there are no active streams
SleepThread (ONE_SECOND / 10);
HibernateThread (ONE_SECOND / 10);
}
else
TaskSwitch ();
+2
View File
@@ -145,6 +145,8 @@ ThreadLocal *CreateThreadLocal (void);
void DestroyThreadLocal (ThreadLocal *tl);
ThreadLocal *GetMyThreadLocal (void);
void HibernateThread (TimePeriod timePeriod);
void HibernateThreadUntil (TimeCount wakeTime);
void SleepThread (TimePeriod timePeriod);
void SleepThreadUntil (TimeCount wakeTime);
void DestroyThread (Thread);
+66 -2
View File
@@ -19,6 +19,7 @@
#include "libs/threadlib.h"
#include "libs/timelib.h"
#include "libs/log.h"
#include "libs/async.h"
#include "libs/memlib.h"
#include "thrcommon.h"
@@ -284,16 +285,79 @@ WaitThread (Thread thread, int *status)
NativeWaitThread (thread, status);
}
#ifdef DEBUG_SLEEP
extern uint32 mainThreadId;
extern uint32 SDL_ThreadID(void);
#endif /* DEBUG_SLEEP */
void
SleepThread (TimePeriod timePeriod)
HibernateThread (TimePeriod timePeriod)
{
#ifdef DEBUG_SLEEP
if (SDL_ThreadID() == mainThreadId)
log_add (log_Debug, "HibernateThread called from main thread.\n");
#endif /* DEBUG_SLEEP */
NativeSleepThread (timePeriod);
}
void
HibernateThreadUntil (TimeCount wakeTime)
{
#ifdef DEBUG_SLEEP
if (SDL_ThreadID() == mainThreadId)
log_add (log_Debug, "HibernateThreadUntil called from main "
"thread.\n");
#endif /* DEBUG_SLEEP */
NativeSleepThreadUntil (wakeTime);
}
void
SleepThread (TimePeriod timePeriod)
{
TimeCount now;
#ifdef DEBUG_SLEEP
if (SDL_ThreadID() != mainThreadId)
log_add (log_Debug, "SleepThread called from non-main "
"thread.\n");
#endif /* DEBUG_SLEEP */
now = GetTimeCounter ();
SleepThreadUntil (now + timePeriod);
}
// Sleep until wakeTime, but call asynchrounous operations until then.
void
SleepThreadUntil (TimeCount wakeTime)
{
NativeSleepThreadUntil (wakeTime);
#ifdef DEBUG_SLEEP
if (SDL_ThreadID() != mainThreadId)
log_add (log_Debug, "SleepThreadUntil called from non-main "
"thread.\n");
#endif /* DEBUG_SLEEP */
for (;;) {
uint32 nextTimeMs;
TimeCount nextTime;
TimeCount now;
Async_process ();
now = GetTimeCounter ();
if (wakeTime <= now)
return;
nextTimeMs = Async_timeBeforeNextMs ();
nextTime = (nextTimeMs / 1000) * ONE_SECOND +
((nextTimeMs % 1000) * ONE_SECOND / 1000);
// Overflow-safe conversion.
if (wakeTime < nextTime)
nextTime = wakeTime;
NativeSleepThreadUntil (nextTime);
}
}
void
+2
View File
@@ -40,6 +40,7 @@
#include "setup.h"
#include "settings.h"
#include "sounds.h"
#include "libs/async.h"
#include "libs/graphics/gfx_common.h"
#include "libs/log.h"
#include "libs/mathlib.h"
@@ -334,6 +335,7 @@ DoBattle (BATTLE_STATE *bs)
battle_speed = HIBYTE (nth_frame);
if (battle_speed == (BYTE)~0)
{ // maximum speed, nothing rendered at all
Async_process ();
TaskSwitch ();
}
else
+7
View File
@@ -83,6 +83,8 @@ DoConfirmExit (void)
if (PlayingTrack ())
PauseTrack ();
PauseFlash ();
LockMutex (GraphicsLock);
{
RECT r;
@@ -160,6 +162,8 @@ DoConfirmExit (void)
}
UnlockMutex (GraphicsLock);
ContinueFlash ();
if (PlayingTrack ())
ResumeTrack ();
@@ -212,6 +216,7 @@ DoPopupWindow (const char *msg)
label.line_count = SplitString (msg, '\n', 30, lines, bank);
label.lines = lines;
PauseFlash ();
LockMutex (GraphicsLock);
oldContext = SetContext (ScreenContext);
@@ -242,6 +247,8 @@ DoPopupWindow (const char *msg)
SetContextClipRect (&oldRect);
SetContext (oldContext);
UnlockMutex (GraphicsLock);
ContinueFlash ();
SetMenuSounds (s0, s1);
StringBank_Free (bank);
}
+2
View File
@@ -28,6 +28,7 @@
#include "sounds.h"
#include "tactrans.h"
#include "uqmdebug.h"
#include "libs/async.h"
#include "libs/inplib.h"
#include "libs/timelib.h"
#include "libs/threadlib.h"
@@ -359,6 +360,7 @@ DoInput (void *pInputState, BOOLEAN resetInput)
do
{
MENU_SOUND_FLAGS soundFlags;
Async_process ();
TaskSwitch ();
UpdateInputState ();
+69 -58
View File
@@ -34,6 +34,7 @@
#include "flash.h"
#include "libs/graphics/gfx_common.h"
#include "libs/tasklib.h"
#include "libs/alarm.h"
#include "libs/log.h"
#include <stdio.h>
@@ -1590,28 +1591,32 @@ DrawAutoPilotMessage (BOOLEAN Reset)
}
static Task flash_task = 0;
static FlashContext *flashContext = NULL;
static RECT flash_rect;
static Mutex flash_mutex = 0;
static Alarm *flashAlarm = NULL;
static BOOLEAN flashPaused = FALSE;
static int
flash_rect_func (void *data)
static void scheduleFlashAlarm (void);
static void
updateFlashRect (void *arg)
{
Task task = (Task)data;
if (flashContext == NULL)
return;
while (!Task_ReadState(task, TASK_EXIT))
{
TimeCount nextTime;
LockMutex (flash_mutex);
Flash_process (flashContext);
nextTime = Flash_nextTime (flashContext);
UnlockMutex (flash_mutex);
SleepThreadUntil (nextTime);
}
scheduleFlashAlarm ();
(void) arg;
}
FinishTask (task);
return 0;
static void
scheduleFlashAlarm (void)
{
TimeCount nextTime = Flash_nextTime (flashContext);
DWORD nextTimeMs = (nextTime / ONE_SECOND) * 1000 +
((nextTime % ONE_SECOND) * 1000 / ONE_SECOND);
// Overflow-safe conversion.
flashAlarm = Alarm_addAbsoluteMs (nextTimeMs, updateFlashRect, NULL);
}
// Pre: the caller holds the GraphicsLock
@@ -1621,10 +1626,6 @@ SetFlashRect (const RECT *pRect)
RECT clip_r = {{0, 0}, {0, 0}};
RECT temp_r;
if (!flash_mutex)
flash_mutex = CreateMutex ("FlashRect Lock",
SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
if (pRect != SFR_MENU_3DO && pRect != SFR_MENU_ANY)
{
// The caller specified their own flash area, or NULL (stop flashing).
@@ -1654,47 +1655,42 @@ SetFlashRect (const RECT *pRect)
}
}
// Conclude an old flash task.
if (flashContext != NULL)
{
UnlockMutex (GraphicsLock);
ConcludeTask (flash_task);
LockMutex (GraphicsLock);
flash_task = 0;
Flash_terminate (flashContext);
flashContext = 0;
}
// Start a new flash task.
if (pRect != 0 && pRect->extent.width != 0)
{
// Flash rectangle is not empty, start or continue flashing.
flash_rect = *pRect;
flash_rect.corner.x += clip_r.corner.x;
flash_rect.corner.y += clip_r.corner.y;
if (flashContext == NULL)
{
// Create a new flash context.
flashContext = Flash_createHighlight (ScreenContext, &flash_rect);
Flash_setMergeFactors(flashContext, 3, 2, 2);
Flash_setSpeed (flashContext, 0, ONE_SECOND / 16, 0, ONE_SECOND / 16);
Flash_setFrameTime (flashContext, ONE_SECOND / 16);
Flash_start (flashContext);
flash_task = AssignTask (flash_rect_func, 2048, "flash rectangle");
scheduleFlashAlarm ();
}
}
// Sleep while calling the flash rectangle update when necessary.
void
FlashRectSleepUntil (TimeCount wakeTime)
{
while (GetTimeCounter() < wakeTime)
else
{
TimeCount flashNextTime = Flash_nextTime (flashContext);
TimeCount nextTime = (flashNextTime < wakeTime) ?
flashNextTime : wakeTime;
SleepThreadUntil (nextTime);
if (nextTime == flashNextTime)
Flash_process (flashContext);
// Reuse an existing flash context
Flash_setRect (flashContext, &flash_rect);
}
}
else
{
// Flash rectangle is empty. Stop flashing.
if (flashContext != NULL)
{
UnlockMutex (GraphicsLock);
Alarm_remove(flashAlarm);
LockMutex (GraphicsLock);
flashAlarm = 0;
Flash_terminate (flashContext);
flashContext = NULL;
}
}
}
@@ -1708,18 +1704,12 @@ COUNT updateFlashRectRecursion = 0;
void
PreUpdateFlashRect (void)
{
if (flash_task)
if (flashAlarm)
{
updateFlashRectRecursion++;
if (updateFlashRectRecursion > 1)
return;
LockMutex (flash_mutex);
Flash_pause (flashContext);
// We need to actually pause, as the flash update function
// is called from another thread.
Flash_preUpdate (flashContext);
UnlockMutex (flash_mutex);
}
}
@@ -1727,16 +1717,13 @@ PreUpdateFlashRect (void)
void
PostUpdateFlashRect (void)
{
if (flash_task)
if (flashAlarm)
{
updateFlashRectRecursion--;
if (updateFlashRectRecursion > 0)
return;
LockMutex (flash_mutex);
Flash_postUpdate (flashContext);
Flash_continue (flashContext);
UnlockMutex (flash_mutex);
}
}
@@ -1760,3 +1747,27 @@ PostUpdateFlashRectLocked (void)
LockMutex (GraphicsLock);
}
// Stop flashing if flashing is active.
void
PauseFlash (void)
{
if (flashContext != NULL)
{
Alarm_remove(flashAlarm);
flashAlarm = 0;
flashPaused = TRUE;
}
}
// Continue flashing after PauseFlash (), if flashing was active.
void
ContinueFlash (void)
{
if (flashPaused)
{
scheduleFlashAlarm ();
flashPaused = FALSE;
}
}
+3
View File
@@ -186,6 +186,9 @@ extern void PreUpdateFlashRect (void);
extern void PostUpdateFlashRect (void);
extern void PreUpdateFlashRectLocked (void);
extern void PostUpdateFlashRectLocked (void);
extern void PauseFlash (void);
extern void ContinueFlash (void);
#define SFR_MENU_3DO ((RECT*)~0L)
#define SFR_MENU_ANY ((RECT*)~1L)
extern void DrawHyperCoords (POINT puniverse);
+8
View File
@@ -50,6 +50,10 @@
#include "options.h"
volatile int MainExited = FALSE;
#ifdef DEBUG_SLEEP
uint32 mainThreadId;
extern uint32 SDL_ThreadID(void);
#endif
// Open or close the periodically occuring QuasiSpace portal.
// It changes the appearant portal size when necessary.
@@ -143,6 +147,10 @@ extern int snddriver, soundflags;
int
Starcon2Main (void *threadArg)
{
#ifdef DEBUG_SLEEP
mainThreadId = SDL_ThreadID();
#endif
#if CREATE_JOURNAL
{
int ac = argc;
+6 -7
View File
@@ -17,7 +17,7 @@
*/
#include "netmelee.h"
#include "libs/alarm.h"
#include "libs/async.h"
#include "libs/callback.h"
#include "libs/log.h"
#include "libs/net.h"
@@ -146,8 +146,7 @@ netInputAux(uint32 timeoutMs) {
NetManager_process(&timeoutMs);
// This may cause more packets to be queued, hence the
// flushPacketQueues().
Alarm_process();
Callback_process();
Async_process();
flushPacketQueues();
// During the flush, a disconnect may be noticed, which triggers
// another callback. It must be handled immediately, before
@@ -166,11 +165,11 @@ netInput(void) {
void
netInputBlocking(uint32 timeoutMs) {
uint32 nextAlarmMs;
uint32 nextAsyncMs;
nextAlarmMs = Alarm_timeBeforeNextMs();
if (nextAlarmMs < timeoutMs)
timeoutMs = nextAlarmMs;
nextAsyncMs = Async_timeBeforeNextMs();
if (nextAsyncMs < timeoutMs)
timeoutMs = nextAsyncMs;
netInputAux(timeoutMs);
}
+2
View File
@@ -37,6 +37,7 @@
#include "../races.h"
#include "../setup.h"
#include "../sounds.h"
#include "libs/async.h"
#include "libs/log.h"
#include "libs/mathlib.h"
@@ -676,6 +677,7 @@ MeleeGameOver (void)
ButtonState = FALSE;
}
Async_process ();
TaskSwitch ();
} while (!(GLOBAL (CurrentActivity) & CHECK_ABORT) && (!ButtonState
&& (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)