diff options
Diffstat (limited to 'Xext/sync.c')
-rw-r--r-- | Xext/sync.c | 2925 |
1 files changed, 2925 insertions, 0 deletions
diff --git a/Xext/sync.c b/Xext/sync.c new file mode 100644 index 0000000..36dd278 --- /dev/null +++ b/Xext/sync.c @@ -0,0 +1,2925 @@ +/* + +Copyright 1991, 1993, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + + +Copyright 1991, 1993 by Digital Equipment Corporation, Maynard, Massachusetts, +and Olivetti Research Limited, Cambridge, England. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Digital or Olivetti +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Digital and Olivetti +make no representations about the suitability of this software +for any purpose. It is provided "as is" without express or implied warranty. + +DIGITAL AND OLIVETTI DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THEY BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +*/ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include <string.h> + +#include <X11/X.h> +#include <X11/Xproto.h> +#include <X11/Xmd.h> +#include "scrnintstr.h" +#include "os.h" +#include "extnsionst.h" +#include "dixstruct.h" +#include "pixmapstr.h" +#include "resource.h" +#include "opaque.h" +#include <X11/extensions/syncproto.h> +#include "syncsrv.h" +#include "syncsdk.h" +#include "protocol-versions.h" + +#include <stdio.h> +#if !defined(WIN32) +#include <sys/time.h> +#endif + +#include "modinit.h" + +/* + * Local Global Variables + */ +static int SyncEventBase; +static int SyncErrorBase; +static RESTYPE RTCounter = 0; +static RESTYPE RTAwait; +static RESTYPE RTAlarm; +static RESTYPE RTAlarmClient; +static RESTYPE RTFence; +static int SyncNumSystemCounters = 0; +static SyncCounter **SysCounterList = NULL; +static int SyncNumInvalidCounterWarnings = 0; +#define MAX_INVALID_COUNTER_WARNINGS 5 + +static const char *WARN_INVALID_COUNTER_COMPARE = +"Warning: Non-counter XSync object using Counter-only\n" +" comparison. Result will never be true.\n"; + +static const char *WARN_INVALID_COUNTER_ALARM = +"Warning: Non-counter XSync object used in alarm. This is\n" +" the result of a programming error in the X server.\n"; + +#define IsSystemCounter(pCounter) \ + (pCounter && (pCounter->sync.client == NULL)) + +/* these are all the alarm attributes that pertain to the alarm's trigger */ +#define XSyncCAAllTrigger \ + (XSyncCACounter | XSyncCAValueType | XSyncCAValue | XSyncCATestType) + +static void SyncComputeBracketValues(SyncCounter *); + +static void SyncInitServerTime(void); + +static void SyncInitIdleTime(void); + +static Bool +SyncCheckWarnIsCounter(const SyncObject* pSync, const char *warning) +{ + if (pSync && (SYNC_COUNTER != pSync->type)) + { + if (SyncNumInvalidCounterWarnings++ < MAX_INVALID_COUNTER_WARNINGS) + { + ErrorF("%s", warning); + ErrorF(" Counter type: %d\n", pSync->type); + } + + return FALSE; + } + + return TRUE; +} + +/* Each counter maintains a simple linked list of triggers that are + * interested in the counter. The two functions below are used to + * delete and add triggers on this list. + */ +static void +SyncDeleteTriggerFromSyncObject(SyncTrigger *pTrigger) +{ + SyncTriggerList *pCur; + SyncTriggerList *pPrev; + SyncCounter *pCounter; + + /* pSync needs to be stored in pTrigger before calling here. */ + + if (!pTrigger->pSync) + return; + + pPrev = NULL; + pCur = pTrigger->pSync->pTriglist; + + while (pCur) + { + if (pCur->pTrigger == pTrigger) + { + if (pPrev) + pPrev->next = pCur->next; + else + pTrigger->pSync->pTriglist = pCur->next; + + free(pCur); + break; + } + + pPrev = pCur; + pCur = pCur->next; + } + + if (SYNC_COUNTER == pTrigger->pSync->type) + { + pCounter = (SyncCounter *)pTrigger->pSync; + + if (IsSystemCounter(pCounter)) + SyncComputeBracketValues(pCounter); + } else if (SYNC_FENCE == pTrigger->pSync->type) { + SyncFence* pFence = (SyncFence*) pTrigger->pSync; + pFence->funcs.DeleteTrigger(pTrigger); + } +} + + +static int +SyncAddTriggerToSyncObject(SyncTrigger *pTrigger) +{ + SyncTriggerList *pCur; + SyncCounter *pCounter; + + if (!pTrigger->pSync) + return Success; + + /* don't do anything if it's already there */ + for (pCur = pTrigger->pSync->pTriglist; pCur; pCur = pCur->next) + { + if (pCur->pTrigger == pTrigger) + return Success; + } + + if (!(pCur = malloc(sizeof(SyncTriggerList)))) + return BadAlloc; + + pCur->pTrigger = pTrigger; + pCur->next = pTrigger->pSync->pTriglist; + pTrigger->pSync->pTriglist = pCur; + + if (SYNC_COUNTER == pTrigger->pSync->type) + { + pCounter = (SyncCounter *)pTrigger->pSync; + + if (IsSystemCounter(pCounter)) + SyncComputeBracketValues(pCounter); + } else if (SYNC_FENCE == pTrigger->pSync->type) { + SyncFence* pFence = (SyncFence*) pTrigger->pSync; + pFence->funcs.AddTrigger(pTrigger); + } + + return Success; +} + + +/* Below are five possible functions that can be plugged into + * pTrigger->CheckTrigger for counter sync objects, corresponding to + * the four possible test-types, and the one possible function that + * can be plugged into pTrigger->CheckTrigger for fence sync objects. + * These functions are called after the sync object's state changes + * but are also passed the old state so they can inspect both the old + * and new values. (PositiveTransition and NegativeTransition need to + * see both pieces of information.) These functions return the truth + * value of the trigger. + * + * All of them include the condition pTrigger->pSync == NULL. + * This is because the spec says that a trigger with a sync value + * of None is always TRUE. + */ + +static Bool +SyncCheckTriggerPositiveComparison(SyncTrigger *pTrigger, CARD64 oldval) +{ + SyncCounter *pCounter; + + /* Non-counter sync objects should never get here because they + * never trigger this comparison. */ + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_COMPARE)) + return FALSE; + + pCounter = (SyncCounter *)pTrigger->pSync; + + return (pCounter == NULL || + XSyncValueGreaterOrEqual(pCounter->value, pTrigger->test_value)); +} + +static Bool +SyncCheckTriggerNegativeComparison(SyncTrigger *pTrigger, CARD64 oldval) +{ + SyncCounter *pCounter; + + /* Non-counter sync objects should never get here because they + * never trigger this comparison. */ + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_COMPARE)) + return FALSE; + + pCounter = (SyncCounter *)pTrigger->pSync; + + return (pCounter == NULL || + XSyncValueLessOrEqual(pCounter->value, pTrigger->test_value)); +} + +static Bool +SyncCheckTriggerPositiveTransition(SyncTrigger *pTrigger, CARD64 oldval) +{ + SyncCounter *pCounter; + + /* Non-counter sync objects should never get here because they + * never trigger this comparison. */ + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_COMPARE)) + return FALSE; + + pCounter = (SyncCounter *)pTrigger->pSync; + + return (pCounter == NULL || + (XSyncValueLessThan(oldval, pTrigger->test_value) && + XSyncValueGreaterOrEqual(pCounter->value, pTrigger->test_value))); +} + +static Bool +SyncCheckTriggerNegativeTransition(SyncTrigger *pTrigger, CARD64 oldval) +{ + SyncCounter *pCounter; + + /* Non-counter sync objects should never get here because they + * never trigger this comparison. */ + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_COMPARE)) + return FALSE; + + pCounter = (SyncCounter *)pTrigger->pSync; + + return (pCounter == NULL || + (XSyncValueGreaterThan(oldval, pTrigger->test_value) && + XSyncValueLessOrEqual(pCounter->value, pTrigger->test_value))); +} + +static Bool +SyncCheckTriggerFence(SyncTrigger *pTrigger, CARD64 unused) +{ + SyncFence* pFence = (SyncFence*) pTrigger->pSync; + (void)unused; + + return (pFence == NULL || + pFence->funcs.CheckTriggered(pFence)); +} + +static int +SyncInitTrigger(ClientPtr client, SyncTrigger *pTrigger, XID syncObject, + RESTYPE resType, Mask changes) +{ + SyncObject *pSync = pTrigger->pSync; + SyncCounter *pCounter = NULL; + int rc; + Bool newSyncObject = FALSE; + + if (changes & XSyncCACounter) + { + if (syncObject == None) + pSync = NULL; + else if (Success != (rc = dixLookupResourceByType ((pointer *)&pSync, + syncObject, resType, client, DixReadAccess))) + { + client->errorValue = syncObject; + return rc; + } + if (pSync != pTrigger->pSync) + { /* new counter for trigger */ + SyncDeleteTriggerFromSyncObject(pTrigger); + pTrigger->pSync = pSync; + newSyncObject = TRUE; + } + } + + /* if system counter, ask it what the current value is */ + + if (pSync && SYNC_COUNTER == pSync->type) + { + pCounter = (SyncCounter *)pSync; + + if (IsSystemCounter(pCounter)) + { + (*pCounter->pSysCounterInfo->QueryValue) ((pointer) pCounter, + &pCounter->value); + } + } + + if (changes & XSyncCAValueType) + { + if (pTrigger->value_type != XSyncRelative && + pTrigger->value_type != XSyncAbsolute) + { + client->errorValue = pTrigger->value_type; + return BadValue; + } + } + + if (changes & XSyncCATestType) + { + + if (pSync && SYNC_FENCE == pSync->type) + { + pTrigger->CheckTrigger = SyncCheckTriggerFence; + } + else + { + /* select appropriate CheckTrigger function */ + + switch (pTrigger->test_type) + { + case XSyncPositiveTransition: + pTrigger->CheckTrigger = SyncCheckTriggerPositiveTransition; + break; + case XSyncNegativeTransition: + pTrigger->CheckTrigger = SyncCheckTriggerNegativeTransition; + break; + case XSyncPositiveComparison: + pTrigger->CheckTrigger = SyncCheckTriggerPositiveComparison; + break; + case XSyncNegativeComparison: + pTrigger->CheckTrigger = SyncCheckTriggerNegativeComparison; + break; + default: + client->errorValue = pTrigger->test_type; + return BadValue; + } + } + } + + if (changes & (XSyncCAValueType | XSyncCAValue)) + { + if (pTrigger->value_type == XSyncAbsolute) + pTrigger->test_value = pTrigger->wait_value; + else /* relative */ + { + Bool overflow; + if (pCounter == NULL) + return BadMatch; + + XSyncValueAdd(&pTrigger->test_value, pCounter->value, + pTrigger->wait_value, &overflow); + if (overflow) + { + client->errorValue = XSyncValueHigh32(pTrigger->wait_value); + return BadValue; + } + } + } + + /* we wait until we're sure there are no errors before registering + * a new counter on a trigger + */ + if (newSyncObject) + { + if ((rc = SyncAddTriggerToSyncObject(pTrigger)) != Success) + return rc; + } + else if (pCounter && IsSystemCounter(pCounter)) + { + SyncComputeBracketValues(pCounter); + } + + return Success; +} + +/* AlarmNotify events happen in response to actions taken on an Alarm or + * the counter used by the alarm. AlarmNotify may be sent to multiple + * clients. The alarm maintains a list of clients interested in events. + */ +static void +SyncSendAlarmNotifyEvents(SyncAlarm *pAlarm) +{ + SyncAlarmClientList *pcl; + xSyncAlarmNotifyEvent ane; + SyncTrigger *pTrigger = &pAlarm->trigger; + SyncCounter *pCounter; + + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_ALARM)) + return; + + pCounter = (SyncCounter *)pTrigger->pSync; + + UpdateCurrentTime(); + + ane.type = SyncEventBase + XSyncAlarmNotify; + ane.kind = XSyncAlarmNotify; + ane.alarm = pAlarm->alarm_id; + if (pTrigger->pSync && SYNC_COUNTER == pTrigger->pSync->type) + { + ane.counter_value_hi = XSyncValueHigh32(pCounter->value); + ane.counter_value_lo = XSyncValueLow32(pCounter->value); + } + else + { /* XXX what else can we do if there's no counter? */ + ane.counter_value_hi = ane.counter_value_lo = 0; + } + + ane.alarm_value_hi = XSyncValueHigh32(pTrigger->test_value); + ane.alarm_value_lo = XSyncValueLow32(pTrigger->test_value); + ane.time = currentTime.milliseconds; + ane.state = pAlarm->state; + + /* send to owner */ + if (pAlarm->events) + WriteEventsToClient(pAlarm->client, 1, (xEvent *) &ane); + + /* send to other interested clients */ + for (pcl = pAlarm->pEventClients; pcl; pcl = pcl->next) + WriteEventsToClient(pcl->client, 1, (xEvent *) &ane); +} + + +/* CounterNotify events only occur in response to an Await. The events + * go only to the Awaiting client. + */ +static void +SyncSendCounterNotifyEvents(ClientPtr client, SyncAwait **ppAwait, + int num_events) +{ + xSyncCounterNotifyEvent *pEvents, *pev; + int i; + + if (client->clientGone) + return; + pev = pEvents = malloc(num_events * sizeof(xSyncCounterNotifyEvent)); + if (!pEvents) + return; + UpdateCurrentTime(); + for (i = 0; i < num_events; i++, ppAwait++, pev++) + { + SyncTrigger *pTrigger = &(*ppAwait)->trigger; + pev->type = SyncEventBase + XSyncCounterNotify; + pev->kind = XSyncCounterNotify; + pev->counter = pTrigger->pSync->id; + pev->wait_value_lo = XSyncValueLow32(pTrigger->test_value); + pev->wait_value_hi = XSyncValueHigh32(pTrigger->test_value); + if (SYNC_COUNTER == pTrigger->pSync->type) + { + SyncCounter *pCounter = (SyncCounter *)pTrigger->pSync; + + pev->counter_value_lo = XSyncValueLow32(pCounter->value); + pev->counter_value_hi = XSyncValueHigh32(pCounter->value); + } + else + { + pev->counter_value_lo = 0; + pev->counter_value_hi = 0; + } + + pev->time = currentTime.milliseconds; + pev->count = num_events - i - 1; /* events remaining */ + pev->destroyed = pTrigger->pSync->beingDestroyed; + } + /* swapping will be taken care of by this */ + WriteEventsToClient(client, num_events, (xEvent *)pEvents); + free(pEvents); +} + + +/* This function is called when an alarm's counter is destroyed. + * It is plugged into pTrigger->CounterDestroyed (for alarm triggers). + */ +static void +SyncAlarmCounterDestroyed(SyncTrigger *pTrigger) +{ + SyncAlarm *pAlarm = (SyncAlarm *)pTrigger; + + pAlarm->state = XSyncAlarmInactive; + SyncSendAlarmNotifyEvents(pAlarm); + pTrigger->pSync = NULL; +} + + +/* This function is called when an alarm "goes off." + * It is plugged into pTrigger->TriggerFired (for alarm triggers). + */ +static void +SyncAlarmTriggerFired(SyncTrigger *pTrigger) +{ + SyncAlarm *pAlarm = (SyncAlarm *)pTrigger; + SyncCounter *pCounter; + CARD64 new_test_value; + + if (!SyncCheckWarnIsCounter(pTrigger->pSync, WARN_INVALID_COUNTER_ALARM)) + return; + + pCounter = (SyncCounter *)pTrigger->pSync; + + /* no need to check alarm unless it's active */ + if (pAlarm->state != XSyncAlarmActive) + return; + + /* " if the counter value is None, or if the delta is 0 and + * the test-type is PositiveComparison or NegativeComparison, + * no change is made to value (test-value) and the alarm + * state is changed to Inactive before the event is generated." + */ + if (pCounter == NULL + || (XSyncValueIsZero(pAlarm->delta) + && (pAlarm->trigger.test_type == XSyncPositiveComparison + || pAlarm->trigger.test_type == XSyncNegativeComparison))) + pAlarm->state = XSyncAlarmInactive; + + new_test_value = pAlarm->trigger.test_value; + + if (pAlarm->state == XSyncAlarmActive) + { + Bool overflow; + CARD64 oldvalue; + SyncTrigger *paTrigger = &pAlarm->trigger; + SyncCounter *paCounter; + + if (!SyncCheckWarnIsCounter(paTrigger->pSync, + WARN_INVALID_COUNTER_ALARM)) + return; + + paCounter = (SyncCounter *)pTrigger->pSync; + + /* "The alarm is updated by repeatedly adding delta to the + * value of the trigger and re-initializing it until it + * becomes FALSE." + */ + oldvalue = paTrigger->test_value; + + /* XXX really should do something smarter here */ + + do + { + XSyncValueAdd(&paTrigger->test_value, paTrigger->test_value, + pAlarm->delta, &overflow); + } while (!overflow && + (*paTrigger->CheckTrigger)(paTrigger, + paCounter->value)); + + new_test_value = paTrigger->test_value; + paTrigger->test_value = oldvalue; + + /* "If this update would cause value to fall outside the range + * for an INT64...no change is made to value (test-value) and + * the alarm state is changed to Inactive before the event is + * generated." + */ + if (overflow) + { + new_test_value = oldvalue; + pAlarm->state = XSyncAlarmInactive; + } + } + /* The AlarmNotify event has to have the "new state of the alarm" + * which we can't be sure of until this point. However, it has + * to have the "old" trigger test value. That's the reason for + * all the newvalue/oldvalue shuffling above. After we send the + * events, give the trigger its new test value. + */ + SyncSendAlarmNotifyEvents(pAlarm); + pTrigger->test_value = new_test_value; +} + + +/* This function is called when an Await unblocks, either as a result + * of the trigger firing OR the counter being destroyed. + * It goes into pTrigger->TriggerFired AND pTrigger->CounterDestroyed + * (for Await triggers). + */ +static void +SyncAwaitTriggerFired(SyncTrigger *pTrigger) +{ + SyncAwait *pAwait = (SyncAwait *)pTrigger; + int numwaits; + SyncAwaitUnion *pAwaitUnion; + SyncAwait **ppAwait; + int num_events = 0; + + pAwaitUnion = (SyncAwaitUnion *)pAwait->pHeader; + numwaits = pAwaitUnion->header.num_waitconditions; + ppAwait = malloc(numwaits * sizeof(SyncAwait *)); + if (!ppAwait) + goto bail; + + pAwait = &(pAwaitUnion+1)->await; + + /* "When a client is unblocked, all the CounterNotify events for + * the Await request are generated contiguously. If count is 0 + * there are no more events to follow for this request. If + * count is n, there are at least n more events to follow." + * + * Thus, it is best to find all the counters for which events + * need to be sent first, so that an accurate count field can + * be stored in the events. + */ + for ( ; numwaits; numwaits--, pAwait++) + { + CARD64 diff; + Bool overflow, diffgreater, diffequal; + + /* "A CounterNotify event with the destroyed flag set to TRUE is + * always generated if the counter for one of the triggers is + * destroyed." + */ + if (pAwait->trigger.pSync->beingDestroyed) + { + ppAwait[num_events++] = pAwait; + continue; + } + + if (SYNC_COUNTER == pAwait->trigger.pSync->type) + { + SyncCounter *pCounter = (SyncCounter *) pAwait->trigger.pSync; + + /* "The difference between the counter and the test value is + * calculated by subtracting the test value from the value of + * the counter." + */ + XSyncValueSubtract(&diff, pCounter->value, + pAwait->trigger.test_value, &overflow); + + /* "If the difference lies outside the range for an INT64, an + * event is not generated." + */ + if (overflow) + continue; + diffgreater = XSyncValueGreaterThan(diff, pAwait->event_threshold); + diffequal = XSyncValueEqual(diff, pAwait->event_threshold); + + /* "If the test-type is PositiveTransition or + * PositiveComparison, a CounterNotify event is generated if + * the difference is at least event-threshold. If the test-type + * is NegativeTransition or NegativeComparison, a CounterNotify + * event is generated if the difference is at most + * event-threshold." + */ + + if ( ((pAwait->trigger.test_type == XSyncPositiveComparison || + pAwait->trigger.test_type == XSyncPositiveTransition) + && (diffgreater || diffequal)) + || + ((pAwait->trigger.test_type == XSyncNegativeComparison || + pAwait->trigger.test_type == XSyncNegativeTransition) + && (!diffgreater) /* less or equal */ + ) + ) + { + ppAwait[num_events++] = pAwait; + } + } + } + if (num_events) + SyncSendCounterNotifyEvents(pAwaitUnion->header.client, ppAwait, + num_events); + free(ppAwait); + +bail: + /* unblock the client */ + AttendClient(pAwaitUnion->header.client); + /* delete the await */ + FreeResource(pAwaitUnion->header.delete_id, RT_NONE); +} + + +/* This function should always be used to change a counter's value so that + * any triggers depending on the counter will be checked. + */ +void +SyncChangeCounter(SyncCounter *pCounter, CARD64 newval) +{ + SyncTriggerList *ptl, *pnext; + CARD64 oldval; + + oldval = pCounter->value; + pCounter->value = newval; + + /* run through triggers to see if any become true */ + for (ptl = pCounter->sync.pTriglist; ptl; ptl = pnext) + { + pnext = ptl->next; + if ((*ptl->pTrigger->CheckTrigger)(ptl->pTrigger, oldval)) + (*ptl->pTrigger->TriggerFired)(ptl->pTrigger); + } + + if (IsSystemCounter(pCounter)) + { + SyncComputeBracketValues(pCounter); + } +} + + +/* loosely based on dix/events.c/EventSelectForWindow */ +static Bool +SyncEventSelectForAlarm(SyncAlarm *pAlarm, ClientPtr client, Bool wantevents) +{ + SyncAlarmClientList *pClients; + + if (client == pAlarm->client) /* alarm owner */ + { + pAlarm->events = wantevents; + return Success; + } + + /* see if the client is already on the list (has events selected) */ + + for (pClients = pAlarm->pEventClients; pClients; + pClients = pClients->next) + { + if (pClients->client == client) + { + /* client's presence on the list indicates desire for + * events. If the client doesn't want events, remove it + * from the list. If the client does want events, do + * nothing, since it's already got them. + */ + if (!wantevents) + { + FreeResource(pClients->delete_id, RT_NONE); + } + return Success; + } + } + + /* if we get here, this client does not currently have + * events selected on the alarm + */ + + if (!wantevents) + /* client doesn't want events, and we just discovered that it + * doesn't have them, so there's nothing to do. + */ + return Success; + + /* add new client to pAlarm->pEventClients */ + + pClients = malloc(sizeof(SyncAlarmClientList)); + if (!pClients) + return BadAlloc; + + /* register it as a resource so it will be cleaned up + * if the client dies + */ + + pClients->delete_id = FakeClientID(client->index); + + /* link it into list after we know all the allocations succeed */ + pClients->next = pAlarm->pEventClients; + pAlarm->pEventClients = pClients; + pClients->client = client; + + if (!AddResource(pClients->delete_id, RTAlarmClient, pAlarm)) + return BadAlloc; + + return Success; +} + +/* + * ** SyncChangeAlarmAttributes ** This is used by CreateAlarm and ChangeAlarm + */ +static int +SyncChangeAlarmAttributes(ClientPtr client, SyncAlarm *pAlarm, Mask mask, + CARD32 *values) +{ + int status; + XSyncCounter counter; + Mask origmask = mask; + + counter = + pAlarm->trigger.pSync ? pAlarm->trigger.pSync->id : None; + + while (mask) + { + int index2 = lowbit(mask); + mask &= ~index2; + switch (index2) + { + case XSyncCACounter: + mask &= ~XSyncCACounter; + /* sanity check in SyncInitTrigger */ + counter = *values++; + break; + + case XSyncCAValueType: + mask &= ~XSyncCAValueType; + /* sanity check in SyncInitTrigger */ + pAlarm->trigger.value_type = *values++; + break; + + case XSyncCAValue: + mask &= ~XSyncCAValue; + XSyncIntsToValue(&pAlarm->trigger.wait_value, values[1], values[0]); + values += 2; + break; + + case XSyncCATestType: + mask &= ~XSyncCATestType; + /* sanity check in SyncInitTrigger */ + pAlarm->trigger.test_type = *values++; + break; + + case XSyncCADelta: + mask &= ~XSyncCADelta; + XSyncIntsToValue(&pAlarm->delta, values[1], values[0]); + values += 2; + break; + + case XSyncCAEvents: + mask &= ~XSyncCAEvents; + if ((*values != xTrue) && (*values != xFalse)) + { + client->errorValue = *values; + return BadValue; + } + status = SyncEventSelectForAlarm(pAlarm, client, + (Bool)(*values++)); + if (status != Success) + return status; + break; + + default: + client->errorValue = mask; + return BadValue; + } + } + + /* "If the test-type is PositiveComparison or PositiveTransition + * and delta is less than zero, or if the test-type is + * NegativeComparison or NegativeTransition and delta is + * greater than zero, a Match error is generated." + */ + if (origmask & (XSyncCADelta|XSyncCATestType)) + { + CARD64 zero; + XSyncIntToValue(&zero, 0); + if ((((pAlarm->trigger.test_type == XSyncPositiveComparison) || + (pAlarm->trigger.test_type == XSyncPositiveTransition)) + && XSyncValueLessThan(pAlarm->delta, zero)) + || + (((pAlarm->trigger.test_type == XSyncNegativeComparison) || + (pAlarm->trigger.test_type == XSyncNegativeTransition)) + && XSyncValueGreaterThan(pAlarm->delta, zero)) + ) + { + return BadMatch; + } + } + + /* postpone this until now, when we're sure nothing else can go wrong */ + if ((status = SyncInitTrigger(client, &pAlarm->trigger, counter, RTCounter, + origmask & XSyncCAAllTrigger)) != Success) + return status; + + /* XXX spec does not really say to do this - needs clarification */ + pAlarm->state = XSyncAlarmActive; + return Success; +} + +static SyncObject * +SyncCreate(ClientPtr client, XID id, unsigned char type) +{ + SyncObject *pSync; + + switch (type) { + case SYNC_COUNTER: + pSync = malloc(sizeof(SyncCounter)); + break; + case SYNC_FENCE: + pSync = (SyncObject*)dixAllocateObjectWithPrivates(SyncFence, + PRIVATE_SYNC_FENCE); + break; + default: + return NULL; + } + + if (!pSync) + return NULL; + + pSync->client = client; + pSync->id = id; + pSync->pTriglist = NULL; + pSync->beingDestroyed = FALSE; + pSync->type = type; + + return pSync; +} + + +static SyncCounter * +SyncCreateCounter(ClientPtr client, XSyncCounter id, CARD64 initialvalue) +{ + SyncCounter *pCounter; + + if (!(pCounter = (SyncCounter *)SyncCreate(client, + id, + SYNC_COUNTER))) + return NULL; + + pCounter->value = initialvalue; + pCounter->pSysCounterInfo = NULL; + + if (!AddResource(id, RTCounter, (pointer) pCounter)) + return NULL; + + return pCounter; +} + +static int FreeCounter(void *, XID); + +/* + * ***** System Counter utilities + */ + +pointer +SyncCreateSystemCounter( + char *name, + CARD64 initial, + CARD64 resolution, + SyncCounterType counterType, + void (*QueryValue)(pointer /* pCounter */, + CARD64 * /* pValue_return */), + void (*BracketValues)(pointer /* pCounter */, + CARD64 * /* pbracket_less */, + CARD64 * /* pbracket_greater */) + ) +{ + SyncCounter *pCounter; + + SysCounterList = realloc(SysCounterList, + (SyncNumSystemCounters+1)*sizeof(SyncCounter *)); + if (!SysCounterList) + return NULL; + + /* this function may be called before SYNC has been initialized, so we + * have to make sure RTCounter is created. + */ + if (RTCounter == 0) + { + RTCounter = CreateNewResourceType(FreeCounter, "SyncCounter"); + if (RTCounter == 0) + { + return NULL; + } + } + + pCounter = SyncCreateCounter(NULL, FakeClientID(0), initial); + + if (pCounter) + { + SysCounterInfo *psci; + + psci = malloc(sizeof(SysCounterInfo)); + if (!psci) + { + FreeResource(pCounter->sync.id, RT_NONE); + return pCounter; + } + pCounter->pSysCounterInfo = psci; + psci->name = name; + psci->resolution = resolution; + psci->counterType = counterType; + psci->QueryValue = QueryValue; + psci->BracketValues = BracketValues; + XSyncMaxValue(&psci->bracket_greater); + XSyncMinValue(&psci->bracket_less); + SysCounterList[SyncNumSystemCounters++] = pCounter; + } + return pCounter; +} + +void +SyncDestroySystemCounter(pointer pSysCounter) +{ + SyncCounter *pCounter = (SyncCounter *)pSysCounter; + FreeResource(pCounter->sync.id, RT_NONE); +} + +static void +SyncComputeBracketValues(SyncCounter *pCounter) +{ + SyncTriggerList *pCur; + SyncTrigger *pTrigger; + SysCounterInfo *psci; + CARD64 *pnewgtval = NULL; + CARD64 *pnewltval = NULL; + SyncCounterType ct; + + if (!pCounter) + return; + + psci = pCounter->pSysCounterInfo; + ct = pCounter->pSysCounterInfo->counterType; + if (ct == XSyncCounterNeverChanges) + return; + + XSyncMaxValue(&psci->bracket_greater); + XSyncMinValue(&psci->bracket_less); + + for (pCur = pCounter->sync.pTriglist; pCur; pCur = pCur->next) + { + pTrigger = pCur->pTrigger; + + if (pTrigger->test_type == XSyncPositiveComparison && + ct != XSyncCounterNeverIncreases) + { + if (XSyncValueLessThan(pCounter->value, pTrigger->test_value) && + XSyncValueLessThan(pTrigger->test_value, + psci->bracket_greater)) + { + psci->bracket_greater = pTrigger->test_value; + pnewgtval = &psci->bracket_greater; + } + } + else if (pTrigger->test_type == XSyncNegativeComparison && + ct != XSyncCounterNeverDecreases) + { + if (XSyncValueGreaterThan(pCounter->value, pTrigger->test_value) && + XSyncValueGreaterThan(pTrigger->test_value, + psci->bracket_less)) + { + psci->bracket_less = pTrigger->test_value; + pnewltval = &psci->bracket_less; + } + } + else if (pTrigger->test_type == XSyncNegativeTransition && + ct != XSyncCounterNeverIncreases) + { + if (XSyncValueGreaterThan(pCounter->value, pTrigger->test_value) && + XSyncValueGreaterThan(pTrigger->test_value, psci->bracket_less)) + { + psci->bracket_less = pTrigger->test_value; + pnewltval = &psci->bracket_less; + } else if (XSyncValueEqual(pCounter->value, pTrigger->test_value) && + XSyncValueLessThan(pTrigger->test_value, + psci->bracket_greater)) + { + /* + * The value is exactly equal to our threshold. We want one + * more event in the positive direction to ensure we pick up + * when the value *exceeds* this threshold. + */ + psci->bracket_greater = pTrigger->test_value; + pnewgtval = &psci->bracket_greater; + } + } + else if (pTrigger->test_type == XSyncPositiveTransition && + ct != XSyncCounterNeverDecreases) + { + if (XSyncValueLessThan(pCounter->value, pTrigger->test_value) && + XSyncValueLessThan(pTrigger->test_value, psci->bracket_greater)) + { + psci->bracket_greater = pTrigger->test_value; + pnewgtval = &psci->bracket_greater; + } else if (XSyncValueEqual(pCounter->value, pTrigger->test_value) && + XSyncValueGreaterThan(pTrigger->test_value, + psci->bracket_less)) + { + /* + * The value is exactly equal to our threshold. We want one + * more event in the negative direction to ensure we pick up + * when the value is less than this threshold. + */ + psci->bracket_less = pTrigger->test_value; + pnewltval = &psci->bracket_less; + } + } + } /* end for each trigger */ + + if (pnewgtval || pnewltval) + { + (*psci->BracketValues)((pointer)pCounter, pnewltval, pnewgtval); + } +} + +/* + * ***** Resource delete functions + */ + +/* ARGSUSED */ +static int +FreeAlarm(void *addr, XID id) +{ + SyncAlarm *pAlarm = (SyncAlarm *) addr; + + pAlarm->state = XSyncAlarmDestroyed; + + SyncSendAlarmNotifyEvents(pAlarm); + + /* delete event selections */ + + while (pAlarm->pEventClients) + FreeResource(pAlarm->pEventClients->delete_id, RT_NONE); + + SyncDeleteTriggerFromSyncObject(&pAlarm->trigger); + + free(pAlarm); + return Success; +} + + +/* + * ** Cleanup after the destruction of a Counter + */ +/* ARGSUSED */ +static int +FreeCounter(void *env, XID id) +{ + SyncCounter *pCounter = (SyncCounter *) env; + SyncTriggerList *ptl, *pnext; + + pCounter->sync.beingDestroyed = TRUE; + /* tell all the counter's triggers that the counter has been destroyed */ + for (ptl = pCounter->sync.pTriglist; ptl; ptl = pnext) + { + (*ptl->pTrigger->CounterDestroyed)(ptl->pTrigger); + pnext = ptl->next; + free(ptl); /* destroy the trigger list as we go */ + } + if (IsSystemCounter(pCounter)) + { + int i, found = 0; + + free(pCounter->pSysCounterInfo); + + /* find the counter in the list of system counters and remove it */ + + if (SysCounterList) + { + for (i = 0; i < SyncNumSystemCounters; i++) + { + if (SysCounterList[i] == pCounter) + { + found = i; + break; + } + } + if (found < (SyncNumSystemCounters-1)) + { + for (i = found; i < SyncNumSystemCounters-1; i++) + { + SysCounterList[i] = SysCounterList[i+1]; + } + } + } + SyncNumSystemCounters--; + } + free(pCounter); + return Success; +} + +/* + * ** Cleanup after Await + */ +/* ARGSUSED */ +static int +FreeAwait(void *addr, XID id) +{ + SyncAwaitUnion *pAwaitUnion = (SyncAwaitUnion *) addr; + SyncAwait *pAwait; + int numwaits; + + pAwait = &(pAwaitUnion+1)->await; /* first await on list */ + + /* remove triggers from counters */ + + for (numwaits = pAwaitUnion->header.num_waitconditions; numwaits; + numwaits--, pAwait++) + { + /* If the counter is being destroyed, FreeCounter will delete + * the trigger list itself, so don't do it here. + */ + SyncObject *pSync = pAwait->trigger.pSync; + if (pSync && !pSync->beingDestroyed) + SyncDeleteTriggerFromSyncObject(&pAwait->trigger); + } + free(pAwaitUnion); + return Success; +} + +/* loosely based on dix/events.c/OtherClientGone */ +static int +FreeAlarmClient(void *value, XID id) +{ + SyncAlarm *pAlarm = (SyncAlarm *)value; + SyncAlarmClientList *pCur, *pPrev; + + for (pPrev = NULL, pCur = pAlarm->pEventClients; + pCur; + pPrev = pCur, pCur = pCur->next) + { + if (pCur->delete_id == id) + { + if (pPrev) + pPrev->next = pCur->next; + else + pAlarm->pEventClients = pCur->next; + free(pCur); + return Success; + } + } + FatalError("alarm client not on event list"); + /*NOTREACHED*/ +} + + +/* + * ***** Proc functions + */ + + +/* + * ** Initialize the extension + */ +static int +ProcSyncInitialize(ClientPtr client) +{ + xSyncInitializeReply rep; + int n; + + REQUEST_SIZE_MATCH(xSyncInitializeReq); + + memset(&rep, 0, sizeof(xSyncInitializeReply)); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.majorVersion = SERVER_SYNC_MAJOR_VERSION; + rep.minorVersion = SERVER_SYNC_MINOR_VERSION; + rep.length = 0; + + if (client->swapped) + { + swaps(&rep.sequenceNumber, n); + } + WriteToClient(client, sizeof(rep), (char *) &rep); + return Success; +} + +/* + * ** Get list of system counters available through the extension + */ +static int +ProcSyncListSystemCounters(ClientPtr client) +{ + xSyncListSystemCountersReply rep; + int i, len; + xSyncSystemCounter *list = NULL, *walklist = NULL; + + REQUEST_SIZE_MATCH(xSyncListSystemCountersReq); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.nCounters = SyncNumSystemCounters; + + for (i = len = 0; i < SyncNumSystemCounters; i++) + { + char *name = SysCounterList[i]->pSysCounterInfo->name; + /* pad to 4 byte boundary */ + len += pad_to_int32(sz_xSyncSystemCounter + strlen(name)); + } + + if (len) + { + walklist = list = malloc(len); + if (!list) + return BadAlloc; + } + + rep.length = bytes_to_int32(len); + + if (client->swapped) + { + char n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swapl(&rep.nCounters, n); + } + + for (i = 0; i < SyncNumSystemCounters; i++) + { + int namelen; + char *pname_in_reply; + SysCounterInfo *psci = SysCounterList[i]->pSysCounterInfo; + + walklist->counter = SysCounterList[i]->sync.id; + walklist->resolution_hi = XSyncValueHigh32(psci->resolution); + walklist->resolution_lo = XSyncValueLow32(psci->resolution); + namelen = strlen(psci->name); + walklist->name_length = namelen; + + if (client->swapped) + { + char n; + swapl(&walklist->counter, n); + swapl(&walklist->resolution_hi, n); + swapl(&walklist->resolution_lo, n); + swaps(&walklist->name_length, n); + } + + pname_in_reply = ((char *)walklist) + sz_xSyncSystemCounter; + strncpy(pname_in_reply, psci->name, namelen); + walklist = (xSyncSystemCounter *) (((char *)walklist) + + pad_to_int32(sz_xSyncSystemCounter + namelen)); + } + + WriteToClient(client, sizeof(rep), (char *) &rep); + if (len) + { + WriteToClient(client, len, (char *) list); + free(list); + } + + return Success; +} + +/* + * ** Set client Priority + */ +static int +ProcSyncSetPriority(ClientPtr client) +{ + REQUEST(xSyncSetPriorityReq); + ClientPtr priorityclient; + int rc; + + REQUEST_SIZE_MATCH(xSyncSetPriorityReq); + + if (stuff->id == None) + priorityclient = client; + else { + rc = dixLookupClient(&priorityclient, stuff->id, client, + DixSetAttrAccess); + if (rc != Success) + return rc; + } + + if (priorityclient->priority != stuff->priority) + { + priorityclient->priority = stuff->priority; + + /* The following will force the server back into WaitForSomething + * so that the change in this client's priority is immediately + * reflected. + */ + isItTimeToYield = TRUE; + dispatchException |= DE_PRIORITYCHANGE; + } + return Success; +} + +/* + * ** Get client Priority + */ +static int +ProcSyncGetPriority(ClientPtr client) +{ + REQUEST(xSyncGetPriorityReq); + xSyncGetPriorityReply rep; + ClientPtr priorityclient; + int rc; + + REQUEST_SIZE_MATCH(xSyncGetPriorityReq); + + if (stuff->id == None) + priorityclient = client; + else { + rc = dixLookupClient(&priorityclient, stuff->id, client, + DixGetAttrAccess); + if (rc != Success) + return rc; + } + + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + rep.priority = priorityclient->priority; + + if (client->swapped) + { + char n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.priority, n); + } + + WriteToClient(client, sizeof(xSyncGetPriorityReply), (char *) &rep); + + return Success; +} + +/* + * ** Create a new counter + */ +static int +ProcSyncCreateCounter(ClientPtr client) +{ + REQUEST(xSyncCreateCounterReq); + CARD64 initial; + + REQUEST_SIZE_MATCH(xSyncCreateCounterReq); + + LEGAL_NEW_RESOURCE(stuff->cid, client); + + XSyncIntsToValue(&initial, stuff->initial_value_lo, stuff->initial_value_hi); + if (!SyncCreateCounter(client, stuff->cid, initial)) + return BadAlloc; + + return Success; +} + +/* + * ** Set Counter value + */ +static int +ProcSyncSetCounter(ClientPtr client) +{ + REQUEST(xSyncSetCounterReq); + SyncCounter *pCounter; + CARD64 newvalue; + int rc; + + REQUEST_SIZE_MATCH(xSyncSetCounterReq); + + rc = dixLookupResourceByType((pointer *)&pCounter, stuff->cid, RTCounter, + client, DixWriteAccess); + if (rc != Success) + return rc; + + if (IsSystemCounter(pCounter)) + { + client->errorValue = stuff->cid; + return BadAccess; + } + + XSyncIntsToValue(&newvalue, stuff->value_lo, stuff->value_hi); + SyncChangeCounter(pCounter, newvalue); + return Success; +} + +/* + * ** Change Counter value + */ +static int +ProcSyncChangeCounter(ClientPtr client) +{ + REQUEST(xSyncChangeCounterReq); + SyncCounter *pCounter; + CARD64 newvalue; + Bool overflow; + int rc; + + REQUEST_SIZE_MATCH(xSyncChangeCounterReq); + + rc = dixLookupResourceByType((pointer *)&pCounter, stuff->cid, RTCounter, + client, DixWriteAccess); + if (rc != Success) + return rc; + + if (IsSystemCounter(pCounter)) + { + client->errorValue = stuff->cid; + return BadAccess; + } + + XSyncIntsToValue(&newvalue, stuff->value_lo, stuff->value_hi); + XSyncValueAdd(&newvalue, pCounter->value, newvalue, &overflow); + if (overflow) + { + /* XXX 64 bit value can't fit in 32 bits; do the best we can */ + client->errorValue = stuff->value_hi; + return BadValue; + } + SyncChangeCounter(pCounter, newvalue); + return Success; +} + +/* + * ** Destroy a counter + */ +static int +ProcSyncDestroyCounter(ClientPtr client) +{ + REQUEST(xSyncDestroyCounterReq); + SyncCounter *pCounter; + int rc; + + REQUEST_SIZE_MATCH(xSyncDestroyCounterReq); + + rc = dixLookupResourceByType((pointer *)&pCounter, stuff->counter, RTCounter, + client, DixDestroyAccess); + if (rc != Success) + return rc; + + if (IsSystemCounter(pCounter)) + { + client->errorValue = stuff->counter; + return BadAccess; + } + FreeResource(pCounter->sync.id, RT_NONE); + return Success; +} + +static SyncAwaitUnion* +SyncAwaitPrologue(ClientPtr client, int items) +{ + SyncAwaitUnion *pAwaitUnion; + + /* all the memory for the entire await list is allocated + * here in one chunk + */ + pAwaitUnion = malloc((items+1) * sizeof(SyncAwaitUnion)); + if (!pAwaitUnion) + return NULL; + + /* first item is the header, remainder are real wait conditions */ + + pAwaitUnion->header.delete_id = FakeClientID(client->index); + pAwaitUnion->header.client = client; + pAwaitUnion->header.num_waitconditions = 0; + + if (!AddResource(pAwaitUnion->header.delete_id, RTAwait, pAwaitUnion)) + return NULL; + + return pAwaitUnion; +} + +static void +SyncAwaitEpilogue(ClientPtr client, int items, SyncAwaitUnion *pAwaitUnion) +{ + SyncAwait *pAwait; + int i; + + IgnoreClient(client); + + /* see if any of the triggers are already true */ + + pAwait = &(pAwaitUnion+1)->await; /* skip over header */ + for (i = 0; i < items; i++, pAwait++) + { + CARD64 value; + + /* don't have to worry about NULL counters because the request + * errors before we get here out if they occur + */ + switch (pAwait->trigger.pSync->type) { + case SYNC_COUNTER: + value = ((SyncCounter *)pAwait->trigger.pSync)->value; + break; + default: + XSyncIntToValue(&value, 0); + } + + if ((*pAwait->trigger.CheckTrigger)(&pAwait->trigger, value)) + { + (*pAwait->trigger.TriggerFired)(&pAwait->trigger); + break; /* once is enough */ + } + } +} + +/* + * ** Await + */ +static int +ProcSyncAwait(ClientPtr client) +{ + REQUEST(xSyncAwaitReq); + int len, items; + int i; + xSyncWaitCondition *pProtocolWaitConds; + SyncAwaitUnion *pAwaitUnion; + SyncAwait *pAwait; + int status; + + REQUEST_AT_LEAST_SIZE(xSyncAwaitReq); + + len = client->req_len << 2; + len -= sz_xSyncAwaitReq; + items = len / sz_xSyncWaitCondition; + + if (items * sz_xSyncWaitCondition != len) + { + return BadLength; + } + if (items == 0) + { + client->errorValue = items; /* XXX protocol change */ + return BadValue; + } + + if (!(pAwaitUnion = SyncAwaitPrologue(client, items))) + return BadAlloc; + + /* don't need to do any more memory allocation for this request! */ + + pProtocolWaitConds = (xSyncWaitCondition *) & stuff[1]; + + pAwait = &(pAwaitUnion+1)->await; /* skip over header */ + for (i = 0; i < items; i++, pProtocolWaitConds++, pAwait++) + { + if (pProtocolWaitConds->counter == None) /* XXX protocol change */ + { + /* this should take care of removing any triggers created by + * this request that have already been registered on sync objects + */ + FreeResource(pAwaitUnion->header.delete_id, RT_NONE); + client->errorValue = pProtocolWaitConds->counter; + return SyncErrorBase + XSyncBadCounter; + } + + /* sanity checks are in SyncInitTrigger */ + pAwait->trigger.pSync = NULL; + pAwait->trigger.value_type = pProtocolWaitConds->value_type; + XSyncIntsToValue(&pAwait->trigger.wait_value, + pProtocolWaitConds->wait_value_lo, + pProtocolWaitConds->wait_value_hi); + pAwait->trigger.test_type = pProtocolWaitConds->test_type; + + status = SyncInitTrigger(client, &pAwait->trigger, + pProtocolWaitConds->counter, RTCounter, + XSyncCAAllTrigger); + if (status != Success) + { + /* this should take care of removing any triggers created by + * this request that have already been registered on sync objects + */ + FreeResource(pAwaitUnion->header.delete_id, RT_NONE); + return status; + } + /* this is not a mistake -- same function works for both cases */ + pAwait->trigger.TriggerFired = SyncAwaitTriggerFired; + pAwait->trigger.CounterDestroyed = SyncAwaitTriggerFired; + XSyncIntsToValue(&pAwait->event_threshold, + pProtocolWaitConds->event_threshold_lo, + pProtocolWaitConds->event_threshold_hi); + pAwait->pHeader = &pAwaitUnion->header; + pAwaitUnion->header.num_waitconditions++; + } + + SyncAwaitEpilogue(client, items, pAwaitUnion); + + return Success; +} + + +/* + * ** Query a counter + */ +static int +ProcSyncQueryCounter(ClientPtr client) +{ + REQUEST(xSyncQueryCounterReq); + xSyncQueryCounterReply rep; + SyncCounter *pCounter; + int rc; + + REQUEST_SIZE_MATCH(xSyncQueryCounterReq); + + rc = dixLookupResourceByType((pointer *)&pCounter, stuff->counter, + RTCounter, client, DixReadAccess); + if (rc != Success) + return rc; + + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + + /* if system counter, ask it what the current value is */ + + if (IsSystemCounter(pCounter)) + { + (*pCounter->pSysCounterInfo->QueryValue) ((pointer) pCounter, + &pCounter->value); + } + + rep.value_hi = XSyncValueHigh32(pCounter->value); + rep.value_lo = XSyncValueLow32(pCounter->value); + if (client->swapped) + { + char n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swapl(&rep.value_hi, n); + swapl(&rep.value_lo, n); + } + WriteToClient(client, sizeof(xSyncQueryCounterReply), (char *) &rep); + return Success; +} + + +/* + * ** Create Alarm + */ +static int +ProcSyncCreateAlarm(ClientPtr client) +{ + REQUEST(xSyncCreateAlarmReq); + SyncAlarm *pAlarm; + int status; + unsigned long len, vmask; + SyncTrigger *pTrigger; + + REQUEST_AT_LEAST_SIZE(xSyncCreateAlarmReq); + + LEGAL_NEW_RESOURCE(stuff->id, client); + + vmask = stuff->valueMask; + len = client->req_len - bytes_to_int32(sizeof(xSyncCreateAlarmReq)); + /* the "extra" call to Ones accounts for the presence of 64 bit values */ + if (len != (Ones(vmask) + Ones(vmask & (XSyncCAValue|XSyncCADelta)))) + return BadLength; + + if (!(pAlarm = malloc(sizeof(SyncAlarm)))) + { + return BadAlloc; + } + + /* set up defaults */ + + pTrigger = &pAlarm->trigger; + pTrigger->pSync = NULL; + pTrigger->value_type = XSyncAbsolute; + XSyncIntToValue(&pTrigger->wait_value, 0L); + pTrigger->test_type = XSyncPositiveComparison; + pTrigger->TriggerFired = SyncAlarmTriggerFired; + pTrigger->CounterDestroyed = SyncAlarmCounterDestroyed; + status = SyncInitTrigger(client, pTrigger, None, RTCounter, + XSyncCAAllTrigger); + if (status != Success) + { + free(pAlarm); + return status; + } + + pAlarm->client = client; + pAlarm->alarm_id = stuff->id; + XSyncIntToValue(&pAlarm->delta, 1L); + pAlarm->events = TRUE; + pAlarm->state = XSyncAlarmInactive; + pAlarm->pEventClients = NULL; + status = SyncChangeAlarmAttributes(client, pAlarm, vmask, + (CARD32 *)&stuff[1]); + if (status != Success) + { + free(pAlarm); + return status; + } + + if (!AddResource(stuff->id, RTAlarm, pAlarm)) + return BadAlloc; + + /* see if alarm already triggered. NULL counter will not trigger + * in CreateAlarm and sets alarm state to Inactive. + */ + + if (!pTrigger->pSync) + { + pAlarm->state = XSyncAlarmInactive; /* XXX protocol change */ + } + else + { + SyncCounter *pCounter; + + if (!SyncCheckWarnIsCounter(pTrigger->pSync, + WARN_INVALID_COUNTER_ALARM)) + { + FreeResource(stuff->id, RT_NONE); + return BadAlloc; + } + + pCounter = (SyncCounter *)pTrigger->pSync; + + if ((*pTrigger->CheckTrigger)(pTrigger, pCounter->value)) + (*pTrigger->TriggerFired)(pTrigger); + } + + return Success; +} + +/* + * ** Change Alarm + */ +static int +ProcSyncChangeAlarm(ClientPtr client) +{ + REQUEST(xSyncChangeAlarmReq); + SyncAlarm *pAlarm; + SyncCounter *pCounter = NULL; + long vmask; + int len, status; + + REQUEST_AT_LEAST_SIZE(xSyncChangeAlarmReq); + + status = dixLookupResourceByType((pointer *)&pAlarm, stuff->alarm, RTAlarm, + client, DixWriteAccess); + if (status != Success) + return status; + + vmask = stuff->valueMask; + len = client->req_len - bytes_to_int32(sizeof(xSyncChangeAlarmReq)); + /* the "extra" call to Ones accounts for the presence of 64 bit values */ + if (len != (Ones(vmask) + Ones(vmask & (XSyncCAValue|XSyncCADelta)))) + return BadLength; + + if ((status = SyncChangeAlarmAttributes(client, pAlarm, vmask, + (CARD32 *)&stuff[1])) != Success) + return status; + + if (SyncCheckWarnIsCounter(pAlarm->trigger.pSync, + WARN_INVALID_COUNTER_ALARM)) + pCounter = (SyncCounter *)pAlarm->trigger.pSync; + + /* see if alarm already triggered. NULL counter WILL trigger + * in ChangeAlarm. + */ + + if (!pCounter || + (*pAlarm->trigger.CheckTrigger)(&pAlarm->trigger, pCounter->value)) + { + (*pAlarm->trigger.TriggerFired)(&pAlarm->trigger); + } + return Success; +} + +static int +ProcSyncQueryAlarm(ClientPtr client) +{ + REQUEST(xSyncQueryAlarmReq); + SyncAlarm *pAlarm; + xSyncQueryAlarmReply rep; + SyncTrigger *pTrigger; + int rc; + + REQUEST_SIZE_MATCH(xSyncQueryAlarmReq); + + rc = dixLookupResourceByType((pointer *)&pAlarm, stuff->alarm, RTAlarm, + client, DixReadAccess); + if (rc != Success) + return rc; + + rep.type = X_Reply; + rep.length = bytes_to_int32(sizeof(xSyncQueryAlarmReply) - sizeof(xGenericReply)); + rep.sequenceNumber = client->sequence; + + pTrigger = &pAlarm->trigger; + rep.counter = (pTrigger->pSync) ? pTrigger->pSync->id : None; + +#if 0 /* XXX unclear what to do, depends on whether relative value-types + * are "consumed" immediately and are considered absolute from then + * on. + */ + rep.value_type = pTrigger->value_type; + rep.wait_value_hi = XSyncValueHigh32(pTrigger->wait_value); + rep.wait_value_lo = XSyncValueLow32(pTrigger->wait_value); +#else + rep.value_type = XSyncAbsolute; + rep.wait_value_hi = XSyncValueHigh32(pTrigger->test_value); + rep.wait_value_lo = XSyncValueLow32(pTrigger->test_value); +#endif + + rep.test_type = pTrigger->test_type; + rep.delta_hi = XSyncValueHigh32(pAlarm->delta); + rep.delta_lo = XSyncValueLow32(pAlarm->delta); + rep.events = pAlarm->events; + rep.state = pAlarm->state; + + if (client->swapped) + { + char n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + swapl(&rep.counter, n); + swapl(&rep.wait_value_hi, n); + swapl(&rep.wait_value_lo, n); + swapl(&rep.test_type, n); + swapl(&rep.delta_hi, n); + swapl(&rep.delta_lo, n); + } + + WriteToClient(client, sizeof(xSyncQueryAlarmReply), (char *) &rep); + return Success; +} + +static int +ProcSyncDestroyAlarm(ClientPtr client) +{ + SyncAlarm *pAlarm; + int rc; + REQUEST(xSyncDestroyAlarmReq); + + REQUEST_SIZE_MATCH(xSyncDestroyAlarmReq); + + rc = dixLookupResourceByType((pointer *)&pAlarm, stuff->alarm, RTAlarm, + client, DixDestroyAccess); + if (rc != Success) + return rc; + + FreeResource(stuff->alarm, RT_NONE); + return Success; +} + +static int +ProcSyncCreateFence(ClientPtr client) +{ + REQUEST(xSyncCreateFenceReq); + DrawablePtr pDraw; + SyncFence *pFence; + int rc; + + REQUEST_SIZE_MATCH(xSyncCreateFenceReq); + + rc = dixLookupDrawable(&pDraw, stuff->d, client, M_ANY, DixGetAttrAccess); + if (rc != Success) + return rc; + + LEGAL_NEW_RESOURCE(stuff->fid, client); + + if (!(pFence = (SyncFence *)SyncCreate(client, + stuff->fid, + SYNC_FENCE))) + return BadAlloc; + + miSyncInitFence(pDraw->pScreen, pFence, stuff->initially_triggered); + + if (!AddResource(stuff->fid, RTFence, (pointer) pFence)) + return BadAlloc; + + return client->noClientException; +} + +static int +FreeFence(void *obj, XID id) +{ + SyncFence *pFence = (SyncFence *) obj; + + miSyncDestroyFence(pFence); + + return Success; +} + +int SyncVerifyFence(SyncFence **ppSyncFence, XID fid, + ClientPtr client, Mask mode) +{ + int rc = dixLookupResourceByType((pointer *)ppSyncFence, fid, RTFence, + client, mode); + + if (rc != Success) + client->errorValue = fid; + + return rc; +} + +static int +ProcSyncTriggerFence(ClientPtr client) +{ + REQUEST(xSyncTriggerFenceReq); + SyncFence *pFence; + int rc; + + REQUEST_SIZE_MATCH(xSyncTriggerFenceReq); + + rc = dixLookupResourceByType((pointer *)&pFence, stuff->fid, RTFence, + client, DixWriteAccess); + if (rc != Success) + return rc; + + miSyncTriggerFence(pFence); + + return client->noClientException; +} + +static int +ProcSyncResetFence(ClientPtr client) +{ + REQUEST(xSyncResetFenceReq); + SyncFence *pFence; + int rc; + + REQUEST_SIZE_MATCH(xSyncResetFenceReq); + + rc = dixLookupResourceByType((pointer *)&pFence, stuff->fid, RTFence, + client, DixWriteAccess); + if (rc != Success) + return rc; + + if (pFence->funcs.CheckTriggered(pFence) != TRUE) + return BadMatch; + + pFence->funcs.Reset(pFence); + + return client->noClientException; +} + +static int +ProcSyncDestroyFence(ClientPtr client) +{ + REQUEST(xSyncDestroyFenceReq); + SyncFence *pFence; + int rc; + + REQUEST_SIZE_MATCH(xSyncDestroyFenceReq); + + rc = dixLookupResourceByType((pointer *)&pFence, stuff->fid, RTFence, + client, DixDestroyAccess); + if (rc != Success) + return rc; + + FreeResource(stuff->fid, RT_NONE); + return client->noClientException; +} + +static int +ProcSyncQueryFence(ClientPtr client) +{ + REQUEST(xSyncQueryFenceReq); + xSyncQueryFenceReply rep; + SyncFence *pFence; + int rc; + + REQUEST_SIZE_MATCH(xSyncQueryFenceReq); + + rc = dixLookupResourceByType((pointer *)&pFence, stuff->fid, + RTFence, client, DixReadAccess); + if (rc != Success) + return rc; + + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + + rep.triggered = pFence->funcs.CheckTriggered(pFence); + + if (client->swapped) + { + char n; + swaps(&rep.sequenceNumber, n); + swapl(&rep.length, n); + } + + WriteToClient(client, sizeof(xSyncQueryFenceReply), (char *) &rep); + return client->noClientException; +} + +static int +ProcSyncAwaitFence(ClientPtr client) +{ + REQUEST(xSyncAwaitFenceReq); + SyncAwaitUnion *pAwaitUnion; + SyncAwait *pAwait; + /* Use CARD32 rather than XSyncFence because XIDs are hard-coded to + * CARD32 in protocol definitions */ + CARD32 *pProtocolFences; + int status; + int len; + int items; + int i; + + REQUEST_AT_LEAST_SIZE(xSyncAwaitFenceReq); + + len = client->req_len << 2; + len -= sz_xSyncAwaitFenceReq; + items = len / sizeof(CARD32); + + if (items * sizeof(CARD32) != len) + { + return BadLength; + } + if (items == 0) + { + client->errorValue = items; + return BadValue; + } + + if (!(pAwaitUnion = SyncAwaitPrologue(client, items))) + return BadAlloc; + + /* don't need to do any more memory allocation for this request! */ + + pProtocolFences = (CARD32 *) & stuff[1]; + + pAwait = &(pAwaitUnion+1)->await; /* skip over header */ + for (i = 0; i < items; i++, pProtocolFences++, pAwait++) + { + if (*pProtocolFences == None) + { + /* this should take care of removing any triggers created by + * this request that have already been registered on sync objects + */ + FreeResource(pAwaitUnion->header.delete_id, RT_NONE); + client->errorValue = *pProtocolFences; + return SyncErrorBase + XSyncBadFence; + } + + pAwait->trigger.pSync = NULL; + /* Provide acceptable values for these unused fields to + * satisfy SyncInitTrigger's validation logic + */ + pAwait->trigger.value_type = XSyncAbsolute; + XSyncIntToValue(&pAwait->trigger.wait_value, 0); + pAwait->trigger.test_type = 0; + + status = SyncInitTrigger(client, &pAwait->trigger, + *pProtocolFences, RTFence, + XSyncCAAllTrigger); + if (status != Success) + { + /* this should take care of removing any triggers created by + * this request that have already been registered on sync objects + */ + FreeResource(pAwaitUnion->header.delete_id, RT_NONE); + return status; + } + /* this is not a mistake -- same function works for both cases */ + pAwait->trigger.TriggerFired = SyncAwaitTriggerFired; + pAwait->trigger.CounterDestroyed = SyncAwaitTriggerFired; + /* event_threshold is unused for fence syncs */ + XSyncIntToValue(&pAwait->event_threshold, 0); + pAwait->pHeader = &pAwaitUnion->header; + pAwaitUnion->header.num_waitconditions++; + } + + SyncAwaitEpilogue(client, items, pAwaitUnion); + + return client->noClientException; +} + +/* + * ** Given an extension request, call the appropriate request procedure + */ +static int +ProcSyncDispatch(ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) + { + case X_SyncInitialize: + return ProcSyncInitialize(client); + case X_SyncListSystemCounters: + return ProcSyncListSystemCounters(client); + case X_SyncCreateCounter: + return ProcSyncCreateCounter(client); + case X_SyncSetCounter: + return ProcSyncSetCounter(client); + case X_SyncChangeCounter: + return ProcSyncChangeCounter(client); + case X_SyncQueryCounter: + return ProcSyncQueryCounter(client); + case X_SyncDestroyCounter: + return ProcSyncDestroyCounter(client); + case X_SyncAwait: + return ProcSyncAwait(client); + case X_SyncCreateAlarm: + return ProcSyncCreateAlarm(client); + case X_SyncChangeAlarm: + return ProcSyncChangeAlarm(client); + case X_SyncQueryAlarm: + return ProcSyncQueryAlarm(client); + case X_SyncDestroyAlarm: + return ProcSyncDestroyAlarm(client); + case X_SyncSetPriority: + return ProcSyncSetPriority(client); + case X_SyncGetPriority: + return ProcSyncGetPriority(client); + case X_SyncCreateFence: + return ProcSyncCreateFence(client); + case X_SyncTriggerFence: + return ProcSyncTriggerFence(client); + case X_SyncResetFence: + return ProcSyncResetFence(client); + case X_SyncDestroyFence: + return ProcSyncDestroyFence(client); + case X_SyncQueryFence: + return ProcSyncQueryFence(client); + case X_SyncAwaitFence: + return ProcSyncAwaitFence(client); + default: + return BadRequest; + } +} + +/* + * Boring Swapping stuff ... + */ + +static int +SProcSyncInitialize(ClientPtr client) +{ + REQUEST(xSyncInitializeReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncInitializeReq); + + return ProcSyncInitialize(client); +} + +static int +SProcSyncListSystemCounters(ClientPtr client) +{ + REQUEST(xSyncListSystemCountersReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncListSystemCountersReq); + + return ProcSyncListSystemCounters(client); +} + +static int +SProcSyncCreateCounter(ClientPtr client) +{ + REQUEST(xSyncCreateCounterReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncCreateCounterReq); + swapl(&stuff->cid, n); + swapl(&stuff->initial_value_lo, n); + swapl(&stuff->initial_value_hi, n); + + return ProcSyncCreateCounter(client); +} + +static int +SProcSyncSetCounter(ClientPtr client) +{ + REQUEST(xSyncSetCounterReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncSetCounterReq); + swapl(&stuff->cid, n); + swapl(&stuff->value_lo, n); + swapl(&stuff->value_hi, n); + + return ProcSyncSetCounter(client); +} + +static int +SProcSyncChangeCounter(ClientPtr client) +{ + REQUEST(xSyncChangeCounterReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncChangeCounterReq); + swapl(&stuff->cid, n); + swapl(&stuff->value_lo, n); + swapl(&stuff->value_hi, n); + + return ProcSyncChangeCounter(client); +} + +static int +SProcSyncQueryCounter(ClientPtr client) +{ + REQUEST(xSyncQueryCounterReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncQueryCounterReq); + swapl(&stuff->counter, n); + + return ProcSyncQueryCounter(client); +} + +static int +SProcSyncDestroyCounter(ClientPtr client) +{ + REQUEST(xSyncDestroyCounterReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncDestroyCounterReq); + swapl(&stuff->counter, n); + + return ProcSyncDestroyCounter(client); +} + +static int +SProcSyncAwait(ClientPtr client) +{ + REQUEST(xSyncAwaitReq); + char n; + + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xSyncAwaitReq); + SwapRestL(stuff); + + return ProcSyncAwait(client); +} + +static int +SProcSyncCreateAlarm(ClientPtr client) +{ + REQUEST(xSyncCreateAlarmReq); + char n; + + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xSyncCreateAlarmReq); + swapl(&stuff->id, n); + swapl(&stuff->valueMask, n); + SwapRestL(stuff); + + return ProcSyncCreateAlarm(client); +} + +static int +SProcSyncChangeAlarm(ClientPtr client) +{ + REQUEST(xSyncChangeAlarmReq); + char n; + + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xSyncChangeAlarmReq); + swapl(&stuff->alarm, n); + swapl(&stuff->valueMask, n); + SwapRestL(stuff); + return ProcSyncChangeAlarm(client); +} + +static int +SProcSyncQueryAlarm(ClientPtr client) +{ + REQUEST(xSyncQueryAlarmReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncQueryAlarmReq); + swapl(&stuff->alarm, n); + + return ProcSyncQueryAlarm(client); +} + +static int +SProcSyncDestroyAlarm(ClientPtr client) +{ + REQUEST(xSyncDestroyAlarmReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncDestroyAlarmReq); + swapl(&stuff->alarm, n); + + return ProcSyncDestroyAlarm(client); +} + +static int +SProcSyncSetPriority(ClientPtr client) +{ + REQUEST(xSyncSetPriorityReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncSetPriorityReq); + swapl(&stuff->id, n); + swapl(&stuff->priority, n); + + return ProcSyncSetPriority(client); +} + +static int +SProcSyncGetPriority(ClientPtr client) +{ + REQUEST(xSyncGetPriorityReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncGetPriorityReq); + swapl(&stuff->id, n); + + return ProcSyncGetPriority(client); +} + +static int +SProcSyncCreateFence(ClientPtr client) +{ + REQUEST(xSyncCreateFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncCreateFenceReq); + swapl(&stuff->fid, n); + + return ProcSyncCreateFence(client); +} + +static int +SProcSyncTriggerFence(ClientPtr client) +{ + REQUEST(xSyncTriggerFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncTriggerFenceReq); + swapl(&stuff->fid, n); + + return ProcSyncTriggerFence(client); +} + +static int +SProcSyncResetFence(ClientPtr client) +{ + REQUEST(xSyncResetFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncResetFenceReq); + swapl(&stuff->fid, n); + + return ProcSyncResetFence(client); +} + +static int +SProcSyncDestroyFence(ClientPtr client) +{ + REQUEST(xSyncDestroyFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncDestroyFenceReq); + swapl(&stuff->fid, n); + + return ProcSyncDestroyFence(client); +} + +static int +SProcSyncQueryFence(ClientPtr client) +{ + REQUEST(xSyncQueryFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH (xSyncQueryFenceReq); + swapl(&stuff->fid, n); + + return ProcSyncQueryFence(client); +} + +static int +SProcSyncAwaitFence(ClientPtr client) +{ + REQUEST(xSyncAwaitFenceReq); + char n; + + swaps(&stuff->length, n); + REQUEST_AT_LEAST_SIZE(xSyncAwaitFenceReq); + SwapRestL(stuff); + + return ProcSyncAwaitFence(client); +} + +static int +SProcSyncDispatch(ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) + { + case X_SyncInitialize: + return SProcSyncInitialize(client); + case X_SyncListSystemCounters: + return SProcSyncListSystemCounters(client); + case X_SyncCreateCounter: + return SProcSyncCreateCounter(client); + case X_SyncSetCounter: + return SProcSyncSetCounter(client); + case X_SyncChangeCounter: + return SProcSyncChangeCounter(client); + case X_SyncQueryCounter: + return SProcSyncQueryCounter(client); + case X_SyncDestroyCounter: + return SProcSyncDestroyCounter(client); + case X_SyncAwait: + return SProcSyncAwait(client); + case X_SyncCreateAlarm: + return SProcSyncCreateAlarm(client); + case X_SyncChangeAlarm: + return SProcSyncChangeAlarm(client); + case X_SyncQueryAlarm: + return SProcSyncQueryAlarm(client); + case X_SyncDestroyAlarm: + return SProcSyncDestroyAlarm(client); + case X_SyncSetPriority: + return SProcSyncSetPriority(client); + case X_SyncGetPriority: + return SProcSyncGetPriority(client); + case X_SyncCreateFence: + return SProcSyncCreateFence(client); + case X_SyncTriggerFence: + return SProcSyncTriggerFence(client); + case X_SyncResetFence: + return SProcSyncResetFence(client); + case X_SyncDestroyFence: + return SProcSyncDestroyFence(client); + case X_SyncQueryFence: + return SProcSyncQueryFence(client); + case X_SyncAwaitFence: + return SProcSyncAwaitFence(client); + default: + return BadRequest; + } +} + +/* + * Event Swapping + */ + +static void +SCounterNotifyEvent(xSyncCounterNotifyEvent *from, xSyncCounterNotifyEvent *to) +{ + to->type = from->type; + to->kind = from->kind; + cpswaps(from->sequenceNumber, to->sequenceNumber); + cpswapl(from->counter, to->counter); + cpswapl(from->wait_value_lo, to->wait_value_lo); + cpswapl(from->wait_value_hi, to->wait_value_hi); + cpswapl(from->counter_value_lo, to->counter_value_lo); + cpswapl(from->counter_value_hi, to->counter_value_hi); + cpswapl(from->time, to->time); + cpswaps(from->count, to->count); + to->destroyed = from->destroyed; +} + + +static void +SAlarmNotifyEvent(xSyncAlarmNotifyEvent *from, xSyncAlarmNotifyEvent *to) +{ + to->type = from->type; + to->kind = from->kind; + cpswaps(from->sequenceNumber, to->sequenceNumber); + cpswapl(from->alarm, to->alarm); + cpswapl(from->counter_value_lo, to->counter_value_lo); + cpswapl(from->counter_value_hi, to->counter_value_hi); + cpswapl(from->alarm_value_lo, to->alarm_value_lo); + cpswapl(from->alarm_value_hi, to->alarm_value_hi); + cpswapl(from->time, to->time); + to->state = from->state; +} + +/* + * ** Close everything down. ** This is fairly simple for now. + */ +/* ARGSUSED */ +static void +SyncResetProc(ExtensionEntry *extEntry) +{ + free(SysCounterList); + SysCounterList = NULL; + RTCounter = 0; +} + +/* + * ** Initialise the extension. + */ +void +SyncExtensionInit(void) +{ + ExtensionEntry *extEntry; + int s; + + for (s = 0; s < screenInfo.numScreens; s++) + miSyncSetup(screenInfo.screens[s]); + + if (RTCounter == 0) + { + RTCounter = CreateNewResourceType(FreeCounter, "SyncCounter"); + } + RTAlarm = CreateNewResourceType(FreeAlarm, "SyncAlarm"); + RTAwait = CreateNewResourceType(FreeAwait, "SyncAwait"); + RTFence = CreateNewResourceType(FreeFence, "SyncFence"); + if (RTAwait) + RTAwait |= RC_NEVERRETAIN; + RTAlarmClient = CreateNewResourceType(FreeAlarmClient, "SyncAlarmClient"); + if (RTAlarmClient) + RTAlarmClient |= RC_NEVERRETAIN; + + if (RTCounter == 0 || RTAwait == 0 || RTAlarm == 0 || + RTAlarmClient == 0 || + (extEntry = AddExtension(SYNC_NAME, + XSyncNumberEvents, XSyncNumberErrors, + ProcSyncDispatch, SProcSyncDispatch, + SyncResetProc, + StandardMinorOpcode)) == NULL) + { + ErrorF("Sync Extension %d.%d failed to Initialise\n", + SYNC_MAJOR_VERSION, SYNC_MINOR_VERSION); + return; + } + + SyncEventBase = extEntry->eventBase; + SyncErrorBase = extEntry->errorBase; + EventSwapVector[SyncEventBase + XSyncCounterNotify] = (EventSwapPtr) SCounterNotifyEvent; + EventSwapVector[SyncEventBase + XSyncAlarmNotify] = (EventSwapPtr) SAlarmNotifyEvent; + + SetResourceTypeErrorValue(RTCounter, SyncErrorBase + XSyncBadCounter); + SetResourceTypeErrorValue(RTAlarm, SyncErrorBase + XSyncBadAlarm); + SetResourceTypeErrorValue(RTFence, SyncErrorBase + XSyncBadFence); + + /* + * Although SERVERTIME is implemented by the OS layer, we initialise it + * here because doing it in OsInit() is too early. The resource database + * is not initialised when OsInit() is called. This is just about OK + * because there is always a servertime counter. + */ + SyncInitServerTime(); + SyncInitIdleTime(); + +#ifdef DEBUG + fprintf(stderr, "Sync Extension %d.%d\n", + SYNC_MAJOR_VERSION, SYNC_MINOR_VERSION); +#endif +} + + +/* + * ***** SERVERTIME implementation - should go in its own file in OS directory? + */ + + + +static pointer ServertimeCounter; +static XSyncValue Now; +static XSyncValue *pnext_time; + +#define GetTime()\ +{\ + unsigned long millis = GetTimeInMillis();\ + unsigned long maxis = XSyncValueHigh32(Now);\ + if (millis < XSyncValueLow32(Now)) maxis++;\ + XSyncIntsToValue(&Now, millis, maxis);\ +} + +/* +*** Server Block Handler +*** code inspired by multibuffer extension (now deprecated) + */ +/*ARGSUSED*/ +static void +ServertimeBlockHandler(void *env, struct timeval **wt, void *LastSelectMask) +{ + XSyncValue delay; + unsigned long timeout; + + if (pnext_time) + { + GetTime(); + + if (XSyncValueGreaterOrEqual(Now, *pnext_time)) + { + timeout = 0; + } + else + { + Bool overflow; + XSyncValueSubtract(&delay, *pnext_time, Now, &overflow); + (void)overflow; + timeout = XSyncValueLow32(delay); + } + AdjustWaitForDelay(wt, timeout); /* os/utils.c */ + } +} + +/* +*** Wakeup Handler + */ +/*ARGSUSED*/ +static void +ServertimeWakeupHandler(void *env, int rc, void *LastSelectMask) +{ + if (pnext_time) + { + GetTime(); + + if (XSyncValueGreaterOrEqual(Now, *pnext_time)) + { + SyncChangeCounter(ServertimeCounter, Now); + } + } +} + +static void +ServertimeQueryValue(void *pCounter, CARD64 *pValue_return) +{ + GetTime(); + *pValue_return = Now; +} + +static void +ServertimeBracketValues(void *pCounter, CARD64 *pbracket_less, + CARD64 *pbracket_greater) +{ + if (!pnext_time && pbracket_greater) + { + RegisterBlockAndWakeupHandlers(ServertimeBlockHandler, + ServertimeWakeupHandler, + NULL); + } + else if (pnext_time && !pbracket_greater) + { + RemoveBlockAndWakeupHandlers(ServertimeBlockHandler, + ServertimeWakeupHandler, + NULL); + } + pnext_time = pbracket_greater; +} + +static void +SyncInitServerTime(void) +{ + CARD64 resolution; + + XSyncIntsToValue(&Now, GetTimeInMillis(), 0); + XSyncIntToValue(&resolution, 4); + ServertimeCounter = SyncCreateSystemCounter("SERVERTIME", Now, resolution, + XSyncCounterNeverDecreases, + ServertimeQueryValue, ServertimeBracketValues); + pnext_time = NULL; +} + + + +/* + * IDLETIME implementation + */ + +static SyncCounter *IdleTimeCounter; +static XSyncValue *pIdleTimeValueLess; +static XSyncValue *pIdleTimeValueGreater; + +static void +IdleTimeQueryValue (pointer pCounter, CARD64 *pValue_return) +{ + CARD32 idle = GetTimeInMillis() - lastDeviceEventTime.milliseconds; + XSyncIntsToValue (pValue_return, idle, 0); +} + +static void +IdleTimeBlockHandler(pointer env, struct timeval **wt, pointer LastSelectMask) +{ + XSyncValue idle, old_idle; + SyncTriggerList *list = IdleTimeCounter->sync.pTriglist; + SyncTrigger *trig; + + if (!pIdleTimeValueLess && !pIdleTimeValueGreater) + return; + + old_idle = IdleTimeCounter->value; + IdleTimeQueryValue (NULL, &idle); + IdleTimeCounter->value = idle; /* push, so CheckTrigger works */ + + if (pIdleTimeValueLess && + XSyncValueLessOrEqual (idle, *pIdleTimeValueLess)) + { + /* + * We've been idle for less than the threshold value, and someone + * wants to know about that, but now we need to know whether they + * want level or edge trigger. Check the trigger list against the + * current idle time, and if any succeed, bomb out of select() + * immediately so we can reschedule. + */ + + for (list = IdleTimeCounter->sync.pTriglist; list; list = list->next) { + trig = list->pTrigger; + if (trig->CheckTrigger(trig, old_idle)) { + AdjustWaitForDelay(wt, 0); + break; + } + } + /* + * We've been called exactly on the idle time, but we have a + * NegativeTransition trigger which requires a transition from an + * idle time greater than this. Schedule a wakeup for the next + * millisecond so we won't miss a transition. + */ + if (XSyncValueEqual (idle, *pIdleTimeValueLess)) + AdjustWaitForDelay(wt, 1); + } + else if (pIdleTimeValueGreater) + { + /* + * There's a threshold in the positive direction. If we've been + * idle less than it, schedule a wakeup for sometime in the future. + * If we've been idle more than it, and someone wants to know about + * that level-triggered, schedule an immediate wakeup. + */ + unsigned long timeout = -1; + + if (XSyncValueLessThan (idle, *pIdleTimeValueGreater)) { + XSyncValue value; + Bool overflow; + + XSyncValueSubtract (&value, *pIdleTimeValueGreater, + idle, &overflow); + timeout = min(timeout, XSyncValueLow32 (value)); + } else { + for (list = IdleTimeCounter->sync.pTriglist; list; list = list->next) { + trig = list->pTrigger; + if (trig->CheckTrigger(trig, old_idle)) { + timeout = min(timeout, 0); + break; + } + } + } + + AdjustWaitForDelay (wt, timeout); + } + + IdleTimeCounter->value = old_idle; /* pop */ +} + +static void +IdleTimeWakeupHandler (pointer env, int rc, pointer LastSelectMask) +{ + XSyncValue idle; + + if (!pIdleTimeValueLess && !pIdleTimeValueGreater) + return; + + IdleTimeQueryValue (NULL, &idle); + + if ((pIdleTimeValueGreater && + XSyncValueGreaterOrEqual (idle, *pIdleTimeValueGreater)) || + (pIdleTimeValueLess && + XSyncValueLessOrEqual (idle, *pIdleTimeValueLess))) + { + SyncChangeCounter (IdleTimeCounter, idle); + } +} + +static void +IdleTimeBracketValues (pointer pCounter, CARD64 *pbracket_less, + CARD64 *pbracket_greater) +{ + Bool registered = (pIdleTimeValueLess || pIdleTimeValueGreater); + + if (registered && !pbracket_less && !pbracket_greater) + { + RemoveBlockAndWakeupHandlers(IdleTimeBlockHandler, + IdleTimeWakeupHandler, + NULL); + } + else if (!registered && (pbracket_less || pbracket_greater)) + { + RegisterBlockAndWakeupHandlers(IdleTimeBlockHandler, + IdleTimeWakeupHandler, + NULL); + } + + pIdleTimeValueGreater = pbracket_greater; + pIdleTimeValueLess = pbracket_less; +} + +static void +SyncInitIdleTime (void) +{ + CARD64 resolution; + XSyncValue idle; + + IdleTimeQueryValue (NULL, &idle); + XSyncIntToValue (&resolution, 4); + + IdleTimeCounter = SyncCreateSystemCounter ("IDLETIME", idle, resolution, + XSyncCounterUnrestricted, + IdleTimeQueryValue, + IdleTimeBracketValues); + + pIdleTimeValueLess = pIdleTimeValueGreater = NULL; +} |