VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ClientTokenHolder.cpp@ 51476

Last change on this file since 51476 was 48431, checked in by vboxsync, 11 years ago

Main/Machine+Session: New generic client session watcher implementation based on token objects, works on all platforms and is used for now on XPCOM. Additionally a better error message when several API clients are racing for a lock, previously it could be quite confusing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.6 KB
Line 
1/** @file
2 *
3 * VirtualBox API client session token holder (in the client process)
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/asm.h>
19#include <iprt/assert.h>
20#include <iprt/log.h>
21#include <iprt/semaphore.h>
22#include <iprt/process.h>
23
24#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
25# include <errno.h>
26# include <sys/types.h>
27# include <sys/stat.h>
28# include <sys/ipc.h>
29# include <sys/sem.h>
30#endif
31
32#include <VBox/com/defs.h>
33
34#include "ClientTokenHolder.h"
35#include "SessionImpl.h"
36
37
38#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
39/** client token holder thread */
40static DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser);
41#endif
42
43
44Session::ClientTokenHolder::ClientTokenHolder()
45{
46 AssertReleaseFailed();
47}
48
49Session::ClientTokenHolder::~ClientTokenHolder()
50{
51 /* release the client token */
52#if defined(RT_OS_WINDOWS)
53
54 if (mSem && mThreadSem)
55 {
56 /*
57 * tell the thread holding the token to release it;
58 * it will close mSem handle
59 */
60 ::SetEvent(mSem);
61 /* wait for the thread to finish */
62 ::WaitForSingleObject(mThreadSem, INFINITE);
63 ::CloseHandle(mThreadSem);
64
65 mThreadSem = NULL;
66 mSem = NULL;
67 mThread = NIL_RTTHREAD;
68 }
69
70#elif defined(RT_OS_OS2)
71
72 if (mThread != NIL_RTTHREAD)
73 {
74 Assert(mSem != NIL_RTSEMEVENT);
75
76 /* tell the thread holding the token to release it */
77 int vrc = RTSemEventSignal(mSem);
78 AssertRC(vrc == NO_ERROR);
79
80 /* wait for the thread to finish */
81 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
82 Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
83
84 mThread = NIL_RTTHREAD;
85 }
86
87 if (mSem != NIL_RTSEMEVENT)
88 {
89 RTSemEventDestroy(mSem);
90 mSem = NIL_RTSEMEVENT;
91 }
92
93#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
94
95 if (mSem >= 0)
96 {
97 ::sembuf sop = { 0, 1, SEM_UNDO };
98 ::semop(mSem, &sop, 1);
99
100 mSem = -1;
101 }
102
103#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
104
105 if (!mToken.isNull())
106 {
107 mToken->Abandon();
108 mToken.setNull();
109 }
110
111#else
112# error "Port me!"
113#endif
114}
115
116#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
117Session::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) :
118 mClientTokenId(strTokenId)
119#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
120Session::ClientTokenHolder::ClientTokenHolder(IToken *aToken) :
121 mToken(aToken)
122#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
123{
124#ifdef CTHSEMTYPE
125 mSem = CTHSEMARG;
126#endif
127#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
128 mThread = NIL_RTTHREAD;
129#endif
130
131#if defined(RT_OS_WINDOWS)
132 mThreadSem = CTHTHREADSEMARG;
133
134 Bstr bstrTokenId(strTokenId);
135
136 /*
137 * Since there is no guarantee that the constructor and destructor will be
138 * called in the same thread, we need a separate thread to hold the token.
139 */
140
141 mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
142 AssertMsgReturnVoid(mThreadSem,
143 ("Cannot create an event sem, err=%d", ::GetLastError()));
144
145 void *data[3];
146 data[0] = (void*)(BSTR)bstrTokenId.raw();
147 data[1] = (void*)mThreadSem;
148 data[2] = 0; /* will get an output from the thread */
149
150 /* create a thread to hold the token until signalled to release it */
151 int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
152 AssertRCReturnVoid(vrc);
153
154 /* wait until thread init is completed */
155 DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE);
156 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));
157 Assert(data[2]);
158
159 if (wrc == WAIT_OBJECT_0 && data[2])
160 {
161 /* memorize the event sem we should signal in close() */
162 mSem = (HANDLE)data[2];
163 }
164 else
165 {
166 ::CloseHandle(mThreadSem);
167 mThreadSem = NULL;
168 }
169#elif defined(RT_OS_OS2)
170 Bstr bstrTokenId(strTokenId);
171
172 /*
173 * Since there is no guarantee that the constructor and destructor will be
174 * called in the same thread, we need a separate thread to hold the token.
175 */
176
177 int vrc = RTSemEventCreate(&mSem);
178 AssertRCReturnVoid(vrc);
179
180 void *data[3];
181 data[0] = (void*)bstrTokenId.raw();
182 data[1] = (void*)mSem;
183 data[2] = (void*)false; /* will get the thread result here */
184
185 /* create a thread to hold the token until signalled to release it */
186 vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data,
187 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
188 AssertRCReturnVoid(vrc);
189 /* wait until thread init is completed */
190 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT);
191 AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);
192
193 /* the thread must succeed */
194 AssertReturnVoid((bool)data[2]);
195
196#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
197
198# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
199 key_t key = RTStrToUInt32(strTokenId.c_str());
200 AssertMsgReturnVoid(key != 0,
201 ("Key value of 0 is not valid for client token"));
202# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
203 char *pszSemName = NULL;
204 RTStrUtf8ToCurrentCP(&pszSemName, strTokenId);
205 key_t key = ::ftok(pszSemName, 'V');
206 RTStrFree(pszSemName);
207# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
208 int s = ::semget(key, 0, 0);
209 AssertMsgReturnVoid(s >= 0,
210 ("Cannot open semaphore, errno=%d", errno));
211
212 /* grab the semaphore */
213 ::sembuf sop = { 0, -1, SEM_UNDO };
214 int rv = ::semop(s, &sop, 1);
215 AssertMsgReturnVoid(rv == 0,
216 ("Cannot grab semaphore, errno=%d", errno));
217 mSem = s;
218
219#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER)
220
221 /* nothing to do */
222
223#else
224# error "Port me!"
225#endif
226}
227
228bool Session::ClientTokenHolder::isReady()
229{
230#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
231 return mSem != CTHSEMARG;
232#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
233 return !mToken.isNull();
234#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
235}
236
237#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
238/** client token holder thread */
239DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser)
240{
241 LogFlowFuncEnter();
242
243 Assert(pvUser);
244
245 void **data = (void **)pvUser;
246
247# if defined(RT_OS_WINDOWS)
248 BSTR sessionId = (BSTR)data[0];
249 HANDLE initDoneSem = (HANDLE)data[1];
250
251 HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);
252 AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError()));
253
254 if (mutex)
255 {
256 /* grab the token */
257 DWORD wrc = ::WaitForSingleObject(mutex, 0);
258 AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc));
259 if (wrc == WAIT_OBJECT_0)
260 {
261 HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);
262 AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
263 if (finishSem)
264 {
265 data[2] = (void*)finishSem;
266 /* signal we're done with init */
267 ::SetEvent(initDoneSem);
268 /* wait until we're signaled to release the token */
269 ::WaitForSingleObject(finishSem, INFINITE);
270 /* release the token */
271 LogFlow(("ClientTokenHolderThread(): releasing token...\n"));
272 BOOL success = ::ReleaseMutex(mutex);
273 AssertMsg(success, ("cannot release token, err=%d\n", ::GetLastError()));
274 ::CloseHandle(mutex);
275 ::CloseHandle(finishSem);
276 }
277 }
278 }
279
280 /* signal we're done */
281 ::SetEvent(initDoneSem);
282# elif defined(RT_OS_OS2)
283 Utf8Str sessionId = (BSTR)data[0];
284 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];
285
286 LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem));
287
288 HMTX mutex = NULLHANDLE;
289 APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex);
290 AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc));
291
292 if (arc == NO_ERROR)
293 {
294 /* grab the token */
295 LogFlowFunc(("grabbing token...\n"));
296 arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN);
297 AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc));
298 if (arc == NO_ERROR)
299 {
300 /* store the answer */
301 data[2] = (void*)true;
302 /* signal we're done */
303 int vrc = RTThreadUserSignal(Thread);
304 AssertRC(vrc);
305
306 /* wait until we're signaled to release the token */
307 LogFlowFunc(("waiting for termination signal..\n"));
308 vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT);
309 Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT);
310
311 /* release the token */
312 LogFlowFunc(("releasing token...\n"));
313 arc = ::DosReleaseMutexSem(mutex);
314 AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc));
315 }
316 ::DosCloseMutexSem(mutex);
317 }
318
319 /* store the answer */
320 data[1] = (void*)false;
321 /* signal we're done */
322 int vrc = RTThreadUserSignal(Thread);
323 AssertRC(vrc);
324# else
325# error "Port me!"
326# endif
327
328 LogFlowFuncLeave();
329
330 return 0;
331}
332#endif
333
334/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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