private void InternalSendEvent(QueuedEvent _event) { if (isFiring) { eventQueue.Enqueue(_event); return; } try { isFiring = true; SingleInternalSendEvent(_event); while (eventQueue.Count > 0) { var queuedEvent = eventQueue.Dequeue(); SingleInternalSendEvent(queuedEvent); eventPool.Enqueue(queuedEvent); } } finally { isFiring = false; eventQueue.Clear(); } }
public void queue(EventData ed, Event ev) { if (!async || ed.getDefinition().isSynchronous()) { ed.dispatch(ev); } else { if (eventDispatcher == null) { // Log.CONTEXT_EVENTS.debug("Cannot queue event '" + ev + "': context manager is not running"); return; } QueuedEvent qe = new QueuedEvent(ed, ev); try { eventDispatcher.queue(qe); } catch (Exception ex1) { // Log.CONTEXT_EVENTS.debug("Interrupted while queueing event: " + ev); } } }
private static void InternalTrackEvent(string eventName, string dataJson, string eventType, string customVariablesJson) { if (!_isInitialized) { var queuedEvent = new QueuedEvent { EventName = eventName, EventDataJson = dataJson, EventType = eventType, EventCustomVariablesJson = customVariablesJson }; AnalyticsLog.Log(TAG, "Add event " + eventName + " to the queue (" + dataJson + ")"); QueuedEvents.Add(queuedEvent); return; } AnalyticsSessionHelper.DefaultHelper().OnNewEvent(); AnalyticsLog.Log(TAG, "Create event " + eventName + " (" + dataJson + ")"); Event.Create(eventName, AnalyticsParameters, dataJson, customVariablesJson, eventType, AnalyticsSessionHelper.DefaultHelper().SessionId, AnalyticsSessionHelper.DefaultHelper().SessionLength, AnalyticsSessionHelper.DefaultHelper().SessionCount, async e => { await Tracker.Instance.TrackEvent(e); }); }
public void queue(QueuedEvent ev) { // FIXME: must implementing: Protecting from deadlocks by prohibiting new event submission from the dispatcher thread undispatchedEvents.enqueue(ev); /* * if (Thread.CurrentThread == this && es != null) // Protecting from deadlocks by prohibiting new event submission from the dispatcher thread * { * es.submit(new Runnable() * { * public void run() * { * try { * undispatchedEvents.put(ev); * } * catch (Exception ex) * { * // Ignoring * } * } * }); * } * else * { * undispatchedEvents.put(ev); * } */ }
private void run() { while (!interrupted) { try { QueuedEvent ev = null; try { ev = undispatchedEvents.dequeue(); } catch (Exception ex) { break; } if (ev != null) { ev.dispatch(); } } catch (Exception ex) { // Normally all errors should be handled in EventData.dispatch(), so there are almost no chances we'll get here Log.CONTEXT_EVENTS.error("Unexpected critical error in event dispatcher", ex); } } }
public void Dispose() { if (!Disposed) { QueuedEvent.New(ClientEvent.Dispose); } }
protected override void QueueEvent(QueuedEvent queuedEvent, bool addToGlobalQueue, bool addToLocalQueue) { using (var proxy = new GenEventingServiceProxy()) { proxy.QueueEvent(new ProxyQueuedEvent(queuedEvent), addToGlobalQueue, addToLocalQueue); } }
// Clean up the queue, removing dispatched objects. // We assume that the called holds the event_queue lock. private static void CleanQueue_Unlocked() { int first_undispatched = 0; while (first_undispatched < event_queue.Count) { QueuedEvent qe = event_queue [first_undispatched] as QueuedEvent; if (!qe.Dispatched) { break; } if (qe.Cookie != 0) { pending_move_cookies.Remove(qe.Cookie); } ++first_undispatched; } if (first_undispatched > 0) { event_queue.RemoveRange(0, first_undispatched); } }
void Update() { mRealtimeSinceStartup = Time.realtimeSinceStartup; while (mDispatchEventQueue != null && mDispatchEventQueue.Count > 0) { QueuedEvent item = mDispatchEventQueue.Dequeue(); if (item == null) { continue; } DoDispatchEvent(item.handler, item.data); // item.handler could have called Dispose if (!ClientIsValid) { return; } } if (!mApplicationFocusLost && mConnected && mCheckConnectionTriggerTime > 0.0f && mRealtimeSinceStartup >= mCheckConnectionTriggerTime) { // Disconnect detected if (DebugEnabled) { //#if DEBUG Debug.Log("[SnipeClient] Update - Disconnect detected"); } //#endif mConnected = false; DisconnectReason = "Update - Disconnect detected"; DisconnectAndDispatch(ConnectionLost); } if (mConnected) { if (mApplicationFocusLost && mApplicationFocusGained) { mApplicationFocusLost = false; if (mHeartbeatEnabled) { ResetHeartbeatTime(true); ResetCheckConnectionTime(); SendPingRequest(); } } else if (mHeartbeatTriggerTime > 0.0f && mRealtimeSinceStartup >= mHeartbeatTriggerTime) { ResetHeartbeatTime(); if (mHeartbeatEnabled) { ResetCheckConnectionTime(); SendPingRequest(); } } } }
public bool GetFront(ref QueuedEvent result) { if (size == 0) { return(false); } result = events[front]; return(true); }
public ProxyQueuedEvent(QueuedEvent oQueuedEvent) { dCreated = oQueuedEvent.Created; sEventTypeName = oQueuedEvent.EventTypeName; sInstanceData = oQueuedEvent.InstanceData; sInstanceName = oQueuedEvent.InstanceName; sInstanceTypeName = oQueuedEvent.InstanceTypeName; iTimestamp = oQueuedEvent.Timestamp; sUsername = oQueuedEvent.UserName; }
public void PairWith(QueuedEvent other) { this.PairedMove = other; other.PairedMove = this; if (this.HoldUntil < other.HoldUntil) { this.HoldUntil = other.HoldUntil; } other.HoldUntil = this.HoldUntil; }
public void Execute(int index) { int count = subscribers.Length; QueuedEvent <T_Event> ev = queuedEvents[index]; for (int i = 0; i < count; i++) { if (subscribers[i].target.Equals(ev.target)) { eventsToProcess.Enqueue(new UnityEventJob(ev.ev, i)); } } }
// Apply high-level processing to the queue. Pair moves, // coalesce events, etc. // We assume that the caller holds the event_queue lock. private static void AnalyzeQueue_Unlocked() { int first_unanalyzed = event_queue.Count; while (first_unanalyzed > 0) { --first_unanalyzed; QueuedEvent qe = event_queue [first_unanalyzed] as QueuedEvent; if (qe.Analyzed) { ++first_unanalyzed; break; } } if (first_unanalyzed == event_queue.Count) { return; } // Walk across the unanalyzed events... for (int i = first_unanalyzed; i < event_queue.Count; ++i) { QueuedEvent qe = event_queue [i] as QueuedEvent; // Pair off the MovedFrom and MovedTo events. if (qe.Cookie != 0) { if ((qe.Type & EventType.MovedFrom) != 0) { pending_move_cookies [qe.Cookie] = qe; // This increases the MovedFrom's HoldUntil time, // giving us more time for the matching MovedTo to // show up. // (512 ms is totally arbitrary) qe.AddMilliseconds(512); } else if ((qe.Type & EventType.MovedTo) != 0) { QueuedEvent paired_move = pending_move_cookies [qe.Cookie] as QueuedEvent; if (paired_move != null) { paired_move.Dispatched = true; qe.PairedMove = paired_move; } } } qe.Analyzed = true; } }
public bool GetFrontAndDequeue(ref QueuedEvent result) { if (size == 0) { return(false); } lock (mutexLock) { result = events[front]; front = (front + 1) % capacity; --size; } return(true); }
public void KeyPressWithModifier(ushort keyAndModifier) { if ((keyAndModifier & KeyboardTables.InvalidFlag) > 0) { // This key is marked as invalid: ignore it. return; } // TODO: other modifiers. if ((keyAndModifier & KeyboardTables.ShiftModifier) > 0) { _Queue.Enqueue(QueuedEvent.CreateKeyDown(KeyboardKey.LeftShift)); } _Queue.Enqueue(QueuedEvent.CreateKeyPress((KeyboardKey)(keyAndModifier & 0x00ff))); if ((keyAndModifier & KeyboardTables.ShiftModifier) > 0) { _Queue.Enqueue(QueuedEvent.CreateKeyUp(KeyboardKey.LeftShift)); } ActionEvent(); }
/// <summary> /// Queue an event to be processed later. /// </summary> /// <param name="target">The entity the event is for.</param> /// <param name="ev">The event to queue.</param> public void QueueEvent(EventTarget target, T_Event ev) { QueuedEvent <T_Event> newEv = new QueuedEvent <T_Event>(target, ev); #if !DISABLE_EVENT_SAFETY_CHKS if (_cachedCurEvents.TryGetValue(newEv, out int index)) { // If avoiding this warning is too annoying then make this the default behaviour and have this be a message sent // when in verbose mode or something Debug.LogWarning( $"To prevent parallel corruption, event {ev.GetType().Name} cannot be sent to the same entity multiple times between processing. This event will replace the previous one!"); _queuedEvents[index] = newEv; } else { _cachedCurEvents[newEv] = _queuedEvents.Length; _queuedEvents.Add(newEv); } #else _queuedEvents.Add(newEv); #endif }
private static unsafe void SnarfWorker() { int event_size = Marshal.SizeOf(typeof(inotify_event)); while (running) { // We get much better performance if we wait a tiny bit // between reads in order to let events build up. // FIXME: We need to be smarter here to avoid queue overflows. Thread.Sleep(15); IntPtr buffer; int nr; // Will block while waiting for events, but with a 1s timeout. inotify_snarf_events(inotify_fd, out nr, out buffer); if (!running) { break; } if (nr == 0) { continue; } ArrayList new_events = new ArrayList(); bool saw_overflow = false; while (nr > 0) { // Read the low-level event struct from the buffer. inotify_event raw_event; raw_event = (inotify_event)Marshal.PtrToStructure(buffer, typeof(inotify_event)); buffer = (IntPtr)((long)buffer + event_size); if ((raw_event.mask & EventType.QueueOverflow) != 0) { saw_overflow = true; } // Now we convert our low-level event struct into a nicer object. QueuedEvent qe = new QueuedEvent(); qe.Wd = raw_event.wd; qe.Type = raw_event.mask; qe.Cookie = raw_event.cookie; // Extract the filename payload (if any) from the buffer. byte [] filename_bytes = new byte[raw_event.len]; Marshal.Copy(buffer, filename_bytes, 0, (int)raw_event.len); buffer = (IntPtr)((long)buffer + raw_event.len); int n_chars = 0; while (n_chars < filename_bytes.Length && filename_bytes [n_chars] != 0) { ++n_chars; } qe.Filename = ""; if (n_chars > 0) { qe.Filename = FileNameMarshaler.LocalToUTF8(filename_bytes, 0, n_chars); } new_events.Add(qe); nr -= event_size + (int)raw_event.len; } if (saw_overflow) { Logger.Log.Warn("Inotify queue overflow!"); } lock (event_queue) { event_queue.AddRange(new_events); Monitor.Pulse(event_queue); } } }
private void DispatchNextQueuedEvent() { QueuedEvent nextEvent = m_queuedEventTriggers.Dequeue(); Dispatch(nextEvent.m_eventType, nextEvent.m_data); }
private void SingleInternalSendEvent(QueuedEvent _eventData) { Event _event = _eventData._event; State from = context.State; switch (context.State) { case State.OffHook: switch (_event) { case Event.CallDialed: if (TransitionTo(State.Ringing, from)) { SwitchState(from, State.Ringing); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.Ringing: switch (_event) { case Event.CallConnected: if (TransitionTo(State.Connected, from)) { SwitchState(from, State.Connected); } break; case Event.HungUp: if (TransitionTo(State.OffHook, from)) { SwitchState(from, State.OffHook); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.Connected: switch (_event) { case Event.LeftMessage: if (TransitionTo(State.DisconnectCall, from)) { SwitchState(from, State.DisconnectCall); } break; case Event.OnHold: if (TransitionTo(State.OnHold, from)) { SwitchState(from, State.OnHold); } break; case Event.HungUp: if (TransitionTo(State.DisconnectCall, from)) { SwitchState(from, State.DisconnectCall); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.OnHold: switch (_event) { case Event.OffHold: if (TransitionTo(State.Connected, from)) { SwitchState(from, State.Connected); } break; case Event.HungUp: if (TransitionTo(State.DisconnectCall, from)) { SwitchState(from, State.DisconnectCall); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.DisconnectCall: switch (_event) { case Event.HungUp: if (TransitionTo(State.OffHook, from)) { SwitchState(from, State.OffHook); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; } }
public void QueueEvent (string messageId, string eventName, string[] parameters, string[] types, string[] values, bool dontLogDescription = false) { if (shutdown) return; if (EventQueue.ContainsKey (messageId)) return; receivedMessages++; lock (EventQueue) { QueuedEvent e = new QueuedEvent (messageId, eventName, parameters, types, values, dontLogDescription); EventQueue[messageId]=e; Environment.Instance.DebugIf ("messages", "[TP][" + Name + "]Queued reception of " + e.ToString () + "."); } }
private void Client_Closed(object sender, EventArgs e) => QueuedEvent.New(ClientEvent.Closed);
protected virtual void Run() { Task.Run(async() => { try { QueuedEvent.OnNew = (e) => { OnClientEvent?.Invoke(e); }; var inFault = false; var running = true; RunOpen(); while (running) { if (await QueuedEvent.Next() is var nextEvent && nextEvent.Ok) { switch (nextEvent.Value) { case ClientEvent.Dispose: RunClose(); running = false; break; case ClientEvent.Opening: break; case ClientEvent.Opened: IsConnected = true; break; case ClientEvent.Restart: case ClientEvent.Faulted: if (!inFault) { inFault = true; Client.Abort(); RunClose(); await Task.Delay(WaitOnFault.Milliseconds); RunOpen(); inFault = false; } break; case ClientEvent.Closing: break; case ClientEvent.Closed: break; } } } } catch (Exception ex) { ex.LogException(); QueuedEvent.New(ClientEvent.Exception); } finally { RunClose(); Disposed = true; QueuedEvent.OnNew = null; } }); }
private void Client_Opened(object sender, EventArgs e) => QueuedEvent.New(ClientEvent.Opened);
private void Client_Faulted(object sender, EventArgs e) => QueuedEvent.New(ClientEvent.Faulted);
private void SingleInternalSendEvent(QueuedEvent _eventData) { Event _event = _eventData._event; State from = context.State; switch (context.State) { case State.WaitForEvent: switch (_event) { case Event.VolumeUp: if (TransitionTo(State.VolumeUp, from)) { SwitchState(from, State.VolumeUp); } break; case Event.VolumeDown: if (TransitionTo(State.VolumeDown, from)) { SwitchState(from, State.VolumeDown); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.VolumeUp: switch (_event) { case Event.VolumeChanged: if (TransitionTo(State.WaitForEvent, from)) { SwitchState(from, State.WaitForEvent); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; case State.VolumeDown: switch (_event) { case Event.VolumeChanged: if (TransitionTo(State.WaitForEvent, from)) { SwitchState(from, State.WaitForEvent); } break; default: if (!HandleInternalActions(from, _event) && !IsEventIgnored(from, _event)) { throw new System.Exception(string.Format("Unhandled event '{0}' in state '{1}'", _event.ToString(), context.State.ToString())); } break; } break; } }
public object DeserializeEvent(QueuedEvent queuedEvent) { return(null); }
/// <summary> /// 処理待ちイベントを構文解析する /// </summary> /// <param name="lexer">字句解析器</param> /// <returns>処理待ちイベント</returns> private static QueuedEvent ParseQueuedEvent(TextLexer lexer) { // = Token token = lexer.GetToken(); if (token.Type != TokenType.Equal) { Log.InvalidToken(LogCategory, token, lexer); return null; } // { token = lexer.GetToken(); if (token.Type != TokenType.OpenBrace) { Log.InvalidToken(LogCategory, token, lexer); return null; } QueuedEvent qe = new QueuedEvent(); while (true) { token = lexer.GetToken(); // ファイルの終端 if (token == null) { break; } // } (セクション終端) if (token.Type == TokenType.CloseBrace) { break; } // 無効なトークン if (token.Type != TokenType.Identifier) { Log.InvalidToken(LogCategory, token, lexer); lexer.SkipLine(); continue; } string keyword = token.Value as string; if (string.IsNullOrEmpty(keyword)) { continue; } keyword = keyword.ToLower(); // tag if (keyword.Equals("tag")) { Country? tag = ParseTag(lexer); if (!tag.HasValue) { Log.InvalidClause(LogCategory, "tag", lexer); continue; } // イベント発生国 qe.Country = (Country) tag; continue; } // id if (keyword.Equals("id")) { int? n = ParseInt(lexer); if (!n.HasValue) { Log.InvalidClause(LogCategory, "id", lexer); continue; } // イベントID qe.Id = (int) n; continue; } // hour if (keyword.Equals("hour")) { int? n = ParseInt(lexer); if (!n.HasValue) { Log.InvalidClause(LogCategory, "hour", lexer); continue; } // イベント発生待ち時間 qe.Hour = (int) n; continue; } // 無効なトークン Log.InvalidToken(LogCategory, token, lexer); lexer.SkipLine(); } return qe; }
private static unsafe void SnarfWorker () { int event_size = Marshal.SizeOf (typeof (inotify_event)); while (running) { // We get much better performance if we wait a tiny bit // between reads in order to let events build up. // FIXME: We need to be smarter here to avoid queue overflows. Thread.Sleep (15); IntPtr buffer; int nr; // Will block while waiting for events, but with a 1s timeout. inotify_snarf_events (inotify_fd, out nr, out buffer); if (!running) break; if (nr == 0) continue; ArrayList new_events = new ArrayList (); bool saw_overflow = false; while (nr > 0) { // Read the low-level event struct from the buffer. inotify_event raw_event; raw_event = (inotify_event) Marshal.PtrToStructure (buffer, typeof (inotify_event)); buffer = (IntPtr) ((long) buffer + event_size); if ((raw_event.mask & EventType.QueueOverflow) != 0) saw_overflow = true; // Now we convert our low-level event struct into a nicer object. QueuedEvent qe = new QueuedEvent (); qe.Wd = raw_event.wd; qe.Type = raw_event.mask; qe.Cookie = raw_event.cookie; // Extract the filename payload (if any) from the buffer. byte [] filename_bytes = new byte[raw_event.len]; Marshal.Copy (buffer, filename_bytes, 0, (int) raw_event.len); buffer = (IntPtr) ((long) buffer + raw_event.len); int n_chars = 0; while (n_chars < filename_bytes.Length && filename_bytes [n_chars] != 0) ++n_chars; qe.Filename = ""; if (n_chars > 0) qe.Filename = FileNameMarshaler.LocalToUTF8 (filename_bytes, 0, n_chars); new_events.Add (qe); nr -= event_size + (int) raw_event.len; } if (saw_overflow) Logger.Log.Warn ("Inotify queue overflow!"); lock (event_queue) { event_queue.AddRange (new_events); Monitor.Pulse (event_queue); } } }
public void PairWith (QueuedEvent other) { this.PairedMove = other; other.PairedMove = this; if (this.HoldUntil < other.HoldUntil) this.HoldUntil = other.HoldUntil; other.HoldUntil = this.HoldUntil; }
static unsafe void SnarfWorker() { Encoding filename_encoding = Encoding.UTF8; int event_size = Marshal.SizeOf (typeof (inotify_event)); while (running) { IntPtr buffer; int nr; // Will block while waiting for events, but with a 1s timeout. inotify_snarf_events (inotify_fd, out nr, out buffer); if (!running) break; if (nr == 0) continue; ArrayList new_events = new ArrayList (); while (nr > 0) { // Read the low-level event struct from the buffer. inotify_event raw_event; raw_event = (inotify_event) Marshal.PtrToStructure (buffer, typeof (inotify_event)); buffer = (IntPtr) ((long) buffer + event_size); // Now we convert our low-level event struct into a nicer object. QueuedEvent qe = new QueuedEvent (); qe.Wd = raw_event.wd; qe.Type = raw_event.mask; qe.Cookie = raw_event.cookie; // Extract the filename payload (if any) from the buffer. byte [] filename_bytes = new byte[raw_event.len]; Marshal.Copy (buffer, filename_bytes, 0, (int) raw_event.len); buffer = (IntPtr) ((long) buffer + raw_event.len); int n_chars = 0; while (n_chars < filename_bytes.Length && filename_bytes [n_chars] != 0) ++n_chars; qe.Filename = ""; if (n_chars > 0) qe.Filename = filename_encoding.GetString (filename_bytes, 0, n_chars); new_events.Add (qe); nr -= event_size + (int) raw_event.len; } lock (event_queue) { event_queue.AddRange (new_events); Monitor.Pulse (event_queue); } } }
public void KeyPress(KeyboardKey key) { _Queue.Enqueue(QueuedEvent.CreateKeyPress(key)); ActionEvent(); }
private static void DispatchWorker() { while (running) { QueuedEvent next_event = null; // Until we find something we want to dispatch, we will stay // inside the following block of code. lock (event_queue) { while (running) { CleanQueue_Unlocked(); AnalyzeQueue_Unlocked(); // Now look for an event to dispatch. DateTime min_hold_until = DateTime.MaxValue; DateTime now = DateTime.Now; foreach (QueuedEvent qe in event_queue) { if (qe.Dispatched) { continue; } if (qe.HoldUntil <= now) { next_event = qe; break; } if (qe.HoldUntil < min_hold_until) { min_hold_until = qe.HoldUntil; } } // If we found an event, break out of this block // and dispatch it. if (next_event != null) { break; } // If we didn't find an event to dispatch, we can sleep // (1) until the next hold-until time // (2) until the lock pulses (which means something changed, so // we need to check that we are still running, new events // are on the queue, etc.) // and then we go back up and try to find something to dispatch // all over again. if (min_hold_until == DateTime.MaxValue) { Monitor.Wait(event_queue); } else { Monitor.Wait(event_queue, min_hold_until - now); } } } // If "running" gets set to false, we might get a null next_event as the above // loop terminates if (next_event == null) { return; } // Now we have an event, so we release the event_queue lock and do // the actual dispatch. // Before we get any further, mark it next_event.Dispatched = true; WatchInfo watched; watched = Lookup(next_event.Wd, next_event.Type); if (watched == null) { continue; } string srcpath = null; // If this event is a paired MoveTo, there is extra work to do. if ((next_event.Type & EventType.MovedTo) != 0 && next_event.PairedMove != null) { WatchInfo paired_watched; paired_watched = Lookup(next_event.PairedMove.Wd, next_event.PairedMove.Type); if (paired_watched != null) { // Set the source path accordingly. srcpath = Path.Combine(paired_watched.Path, next_event.PairedMove.Filename); // Handle the internal rename of the directory. if ((next_event.Type & EventType.IsDirectory) != 0) { HandleMove(srcpath, watched.Path, next_event.Filename); } } } SendEvent(watched, next_event.Filename, srcpath, next_event.Type); // If a directory we are watching gets ignored, we need // to remove it from the watchedByFoo hashes. if ((next_event.Type & EventType.Ignored) != 0) { lock (watched_by_wd) Forget(watched); } } }
public void Delay(short milliseconds) { _Queue.Enqueue(QueuedEvent.CreateDelay(milliseconds)); ActionEvent(); }