VirtualBox

source: vbox/trunk/src/VBox/Main/glue/EventQueue.cpp@ 98188

Last change on this file since 98188 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 6.7 KB
Line 
1/* $Id: EventQueue.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * Event queue class declaration.
4 */
5
6/*
7 * Copyright (C) 2013-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/** @todo Adapt / update documentation! */
29
30#include "VBox/com/EventQueue.h"
31
32#include <iprt/asm.h>
33#include <new> /* For bad_alloc. */
34
35#include <iprt/err.h>
36#include <iprt/semaphore.h>
37#include <iprt/time.h>
38#include <iprt/thread.h>
39#include <iprt/log.h>
40
41namespace com
42{
43
44// EventQueue class
45////////////////////////////////////////////////////////////////////////////////
46
47EventQueue::EventQueue(void)
48 : mUserCnt(0),
49 mShutdown(false)
50{
51 int rc = RTCritSectInit(&mCritSect);
52 AssertRC(rc);
53
54 rc = RTSemEventCreate(&mSemEvent);
55 AssertRC(rc);
56}
57
58EventQueue::~EventQueue(void)
59{
60 int rc = RTCritSectDelete(&mCritSect);
61 AssertRC(rc);
62
63 rc = RTSemEventDestroy(mSemEvent);
64 AssertRC(rc);
65
66 EventQueueListIterator it = mEvents.begin();
67 while (it != mEvents.end())
68 {
69 (*it)->Release();
70 it = mEvents.erase(it);
71 }
72}
73
74/**
75 * Process events pending on this event queue, and wait up to given timeout, if
76 * nothing is available.
77 *
78 * Must be called on same thread this event queue was created on.
79 *
80 * @param cMsTimeout The timeout specified as milliseconds. Use
81 * RT_INDEFINITE_WAIT to wait till an event is posted on the
82 * queue.
83 *
84 * @returns VBox status code
85 * @retval VINF_SUCCESS if one or more messages was processed.
86 * @retval VERR_TIMEOUT if cMsTimeout expired.
87 * @retval VERR_INVALID_CONTEXT if called on the wrong thread.
88 * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called.
89 * On Windows will also be returned when WM_QUIT is encountered.
90 * On Darwin this may also be returned when the native queue is
91 * stopped or destroyed/finished.
92 * @retval VINF_INTERRUPTED if the native system call was interrupted by a
93 * an asynchronous event delivery (signal) or just felt like returning
94 * out of bounds. On darwin it will also be returned if the queue is
95 * stopped.
96 */
97int EventQueue::processEventQueue(RTMSINTERVAL cMsTimeout)
98{
99 size_t cNumEvents;
100 int rc = RTCritSectEnter(&mCritSect);
101 if (RT_SUCCESS(rc))
102 {
103 if (mUserCnt == 0) /* No concurrent access allowed. */
104 {
105 mUserCnt++;
106
107 cNumEvents = mEvents.size();
108 if (!cNumEvents)
109 {
110 int rc2 = RTCritSectLeave(&mCritSect);
111 AssertRC(rc2);
112
113 rc = RTSemEventWaitNoResume(mSemEvent, cMsTimeout);
114
115 rc2 = RTCritSectEnter(&mCritSect);
116 AssertRC(rc2);
117
118 if (RT_SUCCESS(rc))
119 {
120 if (mShutdown)
121 rc = VERR_INTERRUPTED;
122 cNumEvents = mEvents.size();
123 }
124 }
125
126 if (RT_SUCCESS(rc))
127 rc = processPendingEvents(cNumEvents);
128
129 Assert(mUserCnt);
130 mUserCnt--;
131 }
132 else
133 rc = VERR_WRONG_ORDER;
134
135 int rc2 = RTCritSectLeave(&mCritSect);
136 if (RT_SUCCESS(rc))
137 rc = rc2;
138 }
139
140 Assert(rc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT);
141 return rc;
142}
143
144/**
145 * Processes all pending events in the queue at the time of
146 * calling. Note: Does no initial locking, must be done by the
147 * caller!
148 *
149 * @return IPRT status code.
150 */
151int EventQueue::processPendingEvents(size_t cNumEvents)
152{
153 if (!cNumEvents) /* Nothing to process? Bail out early. */
154 return VINF_SUCCESS;
155
156 int rc = VINF_SUCCESS;
157
158 EventQueueListIterator it = mEvents.begin();
159 for (size_t i = 0;
160 i < cNumEvents
161 && it != mEvents.end(); i++)
162 {
163 Event *pEvent = *it;
164 AssertPtr(pEvent);
165
166 mEvents.erase(it);
167
168 int rc2 = RTCritSectLeave(&mCritSect);
169 AssertRC(rc2);
170
171 pEvent->handler();
172 pEvent->Release();
173
174 rc2 = RTCritSectEnter(&mCritSect);
175 AssertRC(rc2);
176
177 it = mEvents.begin();
178 if (mShutdown)
179 {
180 rc = VERR_INTERRUPTED;
181 break;
182 }
183 }
184
185 return rc;
186}
187
188/**
189 * Interrupt thread waiting on event queue processing.
190 *
191 * Can be called on any thread.
192 *
193 * @returns VBox status code.
194 */
195int EventQueue::interruptEventQueueProcessing(void)
196{
197 ASMAtomicWriteBool(&mShutdown, true);
198
199 return RTSemEventSignal(mSemEvent);
200}
201
202/**
203 * Posts an event to this event loop asynchronously.
204 *
205 * @param pEvent the event to post, must be allocated using |new|
206 * @return TRUE if successful and false otherwise
207 */
208BOOL EventQueue::postEvent(Event *pEvent)
209{
210 int rc = RTCritSectEnter(&mCritSect);
211 if (RT_SUCCESS(rc))
212 {
213 try
214 {
215 if (pEvent)
216 {
217 pEvent->AddRef();
218 mEvents.push_back(pEvent);
219 }
220 else /* No locking, since we're already in our crit sect. */
221 mShutdown = true;
222
223 size_t cEvents = mEvents.size();
224 if (cEvents > _1K) /** @todo Make value configurable? */
225 {
226 static int s_cBitchedAboutLotEvents = 0;
227 if (s_cBitchedAboutLotEvents < 10)
228 LogRel(("Warning: Event queue received lots of events (%zu), expect delayed event handling (%d/10)\n",
229 cEvents, ++s_cBitchedAboutLotEvents));
230 }
231
232 /* Leave critical section before signalling event. */
233 rc = RTCritSectLeave(&mCritSect);
234 if (RT_SUCCESS(rc))
235 {
236 int rc2 = RTSemEventSignal(mSemEvent);
237 AssertRC(rc2);
238 }
239 }
240 catch (std::bad_alloc &ba)
241 {
242 NOREF(ba);
243 rc = VERR_NO_MEMORY;
244 }
245
246 if (RT_FAILURE(rc))
247 {
248 int rc2 = RTCritSectLeave(&mCritSect);
249 AssertRC(rc2);
250 }
251 }
252
253 return RT_SUCCESS(rc) ? TRUE : FALSE;
254}
255
256}
257/* namespace com */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette