VirtualBox

source: vbox/trunk/src/VBox/Main/SessionImpl.cpp@ 3254

Last change on this file since 3254 was 3001, checked in by vboxsync, 17 years ago

Main/Frontends: Next step to support asynchronous USB device flow.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.3 KB
Line 
1/** @file
2 *
3 * VBox Client Session COM Class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22#if defined(__WIN__)
23#elif defined(__LINUX__)
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "SessionImpl.h"
35#include "ConsoleImpl.h"
36
37#include "Logging.h"
38
39#include <VBox/err.h>
40#include <iprt/process.h>
41
42#if defined(__WIN__)
43/** VM IPC mutex holder thread */
44static DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser);
45#endif
46
47/**
48 * Local macro to check whether the session is open and return an error if not.
49 * @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this
50 * macro.
51 */
52#define CHECK_OPEN() \
53 do { \
54 if (mState != SessionState_SessionOpen) \
55 return setError (E_UNEXPECTED, \
56 tr ("The session is not open")); \
57 } while (0)
58
59// constructor / destructor
60/////////////////////////////////////////////////////////////////////////////
61
62HRESULT Session::FinalConstruct()
63{
64 LogFlowThisFunc (("\n"));
65
66 return init();
67}
68
69void Session::FinalRelease()
70{
71 LogFlowThisFunc (("\n"));
72
73 uninit (true /* aFinalRelease */);
74}
75
76// public initializer/uninitializer for internal purposes only
77/////////////////////////////////////////////////////////////////////////////
78
79/**
80 * Initializes the Session object.
81 */
82HRESULT Session::init()
83{
84 /* Enclose the state transition NotReady->InInit->Ready */
85 AutoInitSpan autoInitSpan (this);
86 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
87
88 LogFlowThisFuncEnter();
89
90 mState = SessionState_SessionClosed;
91 mType = SessionType_InvalidSessionType;
92
93#if defined(__WIN__)
94 mIPCSem = NULL;
95 mIPCThreadSem = NULL;
96#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
97 mIPCSem = -1;
98#endif
99
100 /* Confirm a successful initialization when it's the case */
101 autoInitSpan.setSucceeded();
102
103 LogFlowThisFuncLeave();
104
105 return S_OK;
106}
107
108/**
109 * Uninitializes the Session object.
110 *
111 * @note Locks this object for writing.
112 */
113void Session::uninit (bool aFinalRelease)
114{
115 LogFlowThisFuncEnter();
116 LogFlowThisFunc (("aFinalRelease=%d\n", aFinalRelease));
117
118 /* Enclose the state transition Ready->InUninit->NotReady */
119 AutoUninitSpan autoUninitSpan (this);
120 if (autoUninitSpan.uninitDone())
121 {
122 LogFlowThisFunc (("Already uninitialized.\n"));
123 LogFlowThisFuncLeave();
124 return;
125 }
126
127 AutoLock alock (this);
128
129 if (mState != SessionState_SessionClosed)
130 {
131 Assert (mState == SessionState_SessionOpen ||
132 mState == SessionState_SessionSpawning);
133
134 HRESULT rc = close (aFinalRelease, false /* aFromServer */);
135 AssertComRC (rc);
136 }
137
138 LogFlowThisFuncLeave();
139}
140
141// ISession properties
142/////////////////////////////////////////////////////////////////////////////
143
144STDMETHODIMP Session::COMGETTER(State) (SessionState_T *aState)
145{
146 if (!aState)
147 return E_POINTER;
148
149 AutoCaller autoCaller (this);
150 CheckComRCReturnRC (autoCaller.rc());
151
152 AutoReaderLock alock (this);
153
154 *aState = mState;
155
156 return S_OK;
157}
158
159STDMETHODIMP Session::COMGETTER(Type) (SessionType_T *aType)
160{
161 if (!aType)
162 return E_POINTER;
163
164 AutoCaller autoCaller (this);
165 CheckComRCReturnRC (autoCaller.rc());
166
167 AutoReaderLock alock (this);
168
169 CHECK_OPEN();
170
171 *aType = mType;
172 return S_OK;
173}
174
175STDMETHODIMP Session::COMGETTER(Machine) (IMachine **aMachine)
176{
177 if (!aMachine)
178 return E_POINTER;
179
180 AutoCaller autoCaller (this);
181 CheckComRCReturnRC (autoCaller.rc());
182
183 AutoReaderLock alock (this);
184
185 CHECK_OPEN();
186
187 HRESULT rc = E_FAIL;
188
189 if (mConsole)
190 rc = mConsole->machine().queryInterfaceTo (aMachine);
191 else
192 rc = mRemoteMachine.queryInterfaceTo (aMachine);
193 ComAssertComRC (rc);
194
195 return rc;
196}
197
198STDMETHODIMP Session::COMGETTER(Console) (IConsole **aConsole)
199{
200 if (!aConsole)
201 return E_POINTER;
202
203 AutoCaller autoCaller (this);
204 CheckComRCReturnRC (autoCaller.rc());
205
206 AutoReaderLock alock (this);
207
208 CHECK_OPEN();
209
210 HRESULT rc = E_FAIL;
211
212 if (mConsole)
213 rc = mConsole.queryInterfaceTo (aConsole);
214 else
215 rc = mRemoteConsole.queryInterfaceTo (aConsole);
216 ComAssertComRC (rc);
217
218 return rc;
219}
220
221// ISession methods
222/////////////////////////////////////////////////////////////////////////////
223
224STDMETHODIMP Session::Close()
225{
226 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
227
228 AutoCaller autoCaller (this);
229 CheckComRCReturnRC (autoCaller.rc());
230
231 /* close() needs write lock */
232 AutoLock alock (this);
233
234 CHECK_OPEN();
235
236 return close (false /* aFinalRelease */, false /* aFromServer */);
237}
238
239// IInternalSessionControl methods
240/////////////////////////////////////////////////////////////////////////////
241
242STDMETHODIMP Session::GetPID (ULONG *aPid)
243{
244 AssertReturn (aPid, E_POINTER);
245
246 AutoCaller autoCaller (this);
247 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
248
249 AutoReaderLock alock (this);
250
251 *aPid = (ULONG) RTProcSelf();
252 AssertCompile (sizeof (*aPid) == sizeof (RTPROCESS));
253
254 return S_OK;
255}
256
257STDMETHODIMP Session::GetRemoteConsole (IConsole **aConsole)
258{
259 AssertReturn (aConsole, E_POINTER);
260
261 AutoCaller autoCaller (this);
262 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
263
264 AutoReaderLock alock (this);
265
266 AssertReturn (mState == SessionState_SessionOpen, E_FAIL);
267
268 AssertMsgReturn (mType == SessionType_DirectSession && !!mConsole,
269 ("This is not a direct session!\n"), E_FAIL);
270
271 mConsole.queryInterfaceTo (aConsole);
272
273 return S_OK;
274}
275
276STDMETHODIMP Session::AssignMachine (IMachine *aMachine)
277{
278 LogFlowThisFuncEnter();
279 LogFlowThisFunc (("aMachine=%p\n", aMachine));
280
281 AutoCaller autoCaller (this);
282 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
283
284 AutoLock alock (this);
285
286 AssertReturn (mState == SessionState_SessionClosed, E_FAIL);
287
288 if (!aMachine)
289 {
290 /*
291 * A special case: the server informs us that this session has been
292 * passed to IVirtualBox::OpenRemoteSession() so this session will
293 * become remote (but not existing) when AssignRemoteMachine() is
294 * called.
295 */
296
297 AssertReturn (mType == SessionType_InvalidSessionType, E_FAIL);
298 mType = SessionType_RemoteSession;
299 mState = SessionState_SessionSpawning;
300
301 LogFlowThisFuncLeave();
302 return S_OK;
303 }
304
305 HRESULT rc = E_FAIL;
306
307 /* query IInternalMachineControl interface */
308 mControl = aMachine;
309 AssertReturn (!!mControl, E_FAIL);
310
311 rc = mConsole.createObject();
312 AssertComRCReturn (rc, rc);
313
314 rc = mConsole->init (aMachine, mControl);
315 AssertComRCReturn (rc, rc);
316
317 rc = grabIPCSemaphore();
318
319 /*
320 * Reference the VirtualBox object to ensure the server is up
321 * until the session is closed
322 */
323 if (SUCCEEDED (rc))
324 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
325
326 if (SUCCEEDED (rc))
327 {
328 mType = SessionType_DirectSession;
329 mState = SessionState_SessionOpen;
330 }
331 else
332 {
333 /* some cleanup */
334 mControl.setNull();
335 mConsole->uninit();
336 mConsole.setNull();
337 }
338
339 LogFlowThisFunc (("rc=%08X\n", rc));
340 LogFlowThisFuncLeave();
341
342 return rc;
343}
344
345STDMETHODIMP Session::AssignRemoteMachine (IMachine *aMachine, IConsole *aConsole)
346{
347 LogFlowThisFuncEnter();
348 LogFlowThisFunc (("aMachine=%p, aConsole=%p\n", aMachine, aConsole));
349
350 AssertReturn (aMachine && aConsole, E_INVALIDARG);
351
352 AutoCaller autoCaller (this);
353 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
354
355 AutoLock alock (this);
356
357 AssertReturn (mState == SessionState_SessionClosed ||
358 mState == SessionState_SessionSpawning, E_FAIL);
359
360 HRESULT rc = E_FAIL;
361
362 /* query IInternalMachineControl interface */
363 mControl = aMachine;
364 AssertReturn (!!mControl, E_FAIL);
365
366 /// @todo (dmik)
367 // currently, the remote session returns the same machine and
368 // console objects as the direct session, thus giving the
369 // (remote) client full control over the direct session. For the
370 // console, it is the desired behavior (the ability to control
371 // VM execution is a must for the remote session). What about
372 // the machine object, we may want to prevent the remote client
373 // from modifying machine data. In this case, we must:
374 // 1) assign the Machine object (instead of the SessionMachine
375 // object that is passed to this method) to mRemoteMachine;
376 // 2) remove GetMachine() property from the IConsole interface
377 // because it always returns the SessionMachine object
378 // (alternatively, we can supply a separate IConsole
379 // implementation that will return the Machine object in
380 // response to GetMachine()).
381
382 mRemoteMachine = aMachine;
383 mRemoteConsole = aConsole;
384
385 /*
386 * Reference the VirtualBox object to ensure the server is up
387 * until the session is closed
388 */
389 rc = aMachine->COMGETTER(Parent) (mVirtualBox.asOutParam());
390
391 if (SUCCEEDED (rc))
392 {
393 /*
394 * RemoteSession type can be already set by AssignMachine() when its
395 * argument is NULL (a special case)
396 */
397 if (mType != SessionType_RemoteSession)
398 mType = SessionType_ExistingSession;
399 else
400 Assert (mState == SessionState_SessionSpawning);
401
402 mState = SessionState_SessionOpen;
403 }
404 else
405 {
406 /* some cleanup */
407 mControl.setNull();
408 mRemoteMachine.setNull();
409 mRemoteConsole.setNull();
410 }
411
412 LogFlowThisFunc (("rc=%08X\n", rc));
413 LogFlowThisFuncLeave();
414
415 return rc;
416}
417
418STDMETHODIMP Session::UpdateMachineState (MachineState_T aMachineState)
419{
420 AutoCaller autoCaller (this);
421
422 if (autoCaller.state() != Ready)
423 {
424 /*
425 * We might have already entered Session::uninit() at this point, so
426 * return silently (not interested in the state change during uninit)
427 */
428 LogFlowThisFunc (("Already uninitialized.\n"));
429 return S_OK;
430 }
431
432 AutoReaderLock alock (this);
433
434 if (mState == SessionState_SessionClosing)
435 {
436 LogFlowThisFunc (("Already being closed.\n"));
437 return S_OK;
438 }
439
440 AssertReturn (mState == SessionState_SessionOpen &&
441 mType == SessionType_DirectSession, E_FAIL);
442
443 AssertReturn (!mControl.isNull(), E_FAIL);
444 AssertReturn (!mConsole.isNull(), E_FAIL);
445
446 return mConsole->updateMachineState (aMachineState);
447}
448
449STDMETHODIMP Session::Uninitialize()
450{
451 LogFlowThisFuncEnter();
452
453 AutoCaller autoCaller (this);
454
455 HRESULT rc = S_OK;
456
457 if (autoCaller.state() == Ready)
458 {
459 AutoReaderLock alock (this);
460
461 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
462
463 if (mState == SessionState_SessionClosing)
464 {
465 LogFlowThisFunc (("Already being closed.\n"));
466 return S_OK;
467 }
468
469 AssertReturn (mState == SessionState_SessionOpen, E_FAIL);
470
471 /* close ourselves */
472 rc = close (false /* aFinalRelease */, true /* aFromServer */);
473 }
474 else if (autoCaller.state() == InUninit)
475 {
476 /*
477 * We might have already entered Session::uninit() at this point,
478 * return silently
479 */
480 LogFlowThisFunc (("Already uninitialized.\n"));
481 }
482 else
483 {
484 LogWarningThisFunc (("UNEXPECTED uninitialization!\n"));
485 rc = autoCaller.rc();
486 }
487
488 LogFlowThisFunc (("rc=%08X\n", rc));
489 LogFlowThisFuncLeave();
490
491 return rc;
492}
493
494STDMETHODIMP Session::OnDVDDriveChange()
495{
496 LogFlowThisFunc (("\n"));
497
498 AutoCaller autoCaller (this);
499 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
500
501 AutoReaderLock alock (this);
502 AssertReturn (mState == SessionState_SessionOpen &&
503 mType == SessionType_DirectSession, E_FAIL);
504
505 return mConsole->onDVDDriveChange();
506}
507
508STDMETHODIMP Session::OnFloppyDriveChange()
509{
510 LogFlowThisFunc (("\n"));
511
512 AutoCaller autoCaller (this);
513 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
514
515 AutoReaderLock alock (this);
516 AssertReturn (mState == SessionState_SessionOpen &&
517 mType == SessionType_DirectSession, E_FAIL);
518
519 return mConsole->onFloppyDriveChange();
520}
521
522STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter)
523{
524 LogFlowThisFunc (("\n"));
525
526 AutoCaller autoCaller (this);
527 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
528
529 AutoReaderLock alock (this);
530 AssertReturn (mState == SessionState_SessionOpen &&
531 mType == SessionType_DirectSession, E_FAIL);
532
533 return mConsole->onNetworkAdapterChange(networkAdapter);
534}
535
536STDMETHODIMP Session::OnVRDPServerChange()
537{
538 LogFlowThisFunc (("\n"));
539
540 AutoCaller autoCaller (this);
541 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
542
543 AutoReaderLock alock (this);
544 AssertReturn (mState == SessionState_SessionOpen &&
545 mType == SessionType_DirectSession, E_FAIL);
546
547 return mConsole->onVRDPServerChange();
548}
549
550STDMETHODIMP Session::OnUSBControllerChange()
551{
552 LogFlowThisFunc (("\n"));
553
554 AutoCaller autoCaller (this);
555 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
556
557 AutoReaderLock alock (this);
558 AssertReturn (mState == SessionState_SessionOpen &&
559 mType == SessionType_DirectSession, E_FAIL);
560
561 return mConsole->onUSBControllerChange();
562}
563
564STDMETHODIMP Session::OnUSBDeviceAttach (IUSBDevice *aDevice,
565 IVirtualBoxErrorInfo *aError)
566{
567 LogFlowThisFunc (("\n"));
568
569 AutoCaller autoCaller (this);
570 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
571
572 AutoReaderLock alock (this);
573 AssertReturn (mState == SessionState_SessionOpen &&
574 mType == SessionType_DirectSession, E_FAIL);
575
576 return mConsole->onUSBDeviceAttach (aDevice, aError);
577}
578
579STDMETHODIMP Session::OnUSBDeviceDetach (INPTR GUIDPARAM aId,
580 IVirtualBoxErrorInfo *aError)
581{
582 LogFlowThisFunc (("\n"));
583
584 AutoCaller autoCaller (this);
585 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
586
587 AutoReaderLock alock (this);
588 AssertReturn (mState == SessionState_SessionOpen &&
589 mType == SessionType_DirectSession, E_FAIL);
590
591 return mConsole->onUSBDeviceDetach (aId, aError);
592}
593
594STDMETHODIMP Session::OnShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)
595{
596 AutoCaller autoCaller (this);
597 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
598
599 AutoReaderLock alock (this);
600 AssertReturn (mState == SessionState_SessionOpen &&
601 mType == SessionType_DirectSession, E_FAIL);
602
603 return mConsole->onShowWindow (aCheck, aCanShow, aWinId);
604}
605
606// private methods
607///////////////////////////////////////////////////////////////////////////////
608
609/**
610 * Closes the current session.
611 *
612 * @param aFinalRelease called as a result of FinalRelease()
613 * @param aFromServer called as a result of Uninitialize()
614 *
615 * @note To be called only from #uninit(), #Close() or #Uninitialize().
616 * @note Locks this object for writing.
617 */
618HRESULT Session::close (bool aFinalRelease, bool aFromServer)
619{
620 LogFlowThisFuncEnter();
621 LogFlowThisFunc (("aFinalRelease=%d, isFromServer=%d\n",
622 aFinalRelease, aFromServer));
623
624 AutoCaller autoCaller (this);
625 AssertComRCReturnRC (autoCaller.rc());
626
627 AutoLock alock (this);
628
629 LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
630
631 if (mState != SessionState_SessionOpen)
632 {
633 Assert (mState == SessionState_SessionSpawning);
634
635 /* The session object is going to be uninitialized by the client before
636 * it has been assigned a direct console of the machine the client
637 * requested to open a remote session to using IVirtualBox::
638 * openRemoteSession(). Theoretically it should not happen because
639 * openRemoteSession() doesn't return control to the client until the
640 * procedure is fully complete, so assert here. */
641 AssertFailed();
642
643 mState = SessionState_SessionClosed;
644 mType = SessionType_InvalidSessionType;
645#if defined(__WIN__)
646 Assert (!mIPCSem && !mIPCThreadSem);
647#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
648 Assert (mIPCSem == -1);
649#endif
650 LogFlowThisFuncLeave();
651 return S_OK;
652 }
653
654 /* go to the closing state */
655 mState = SessionState_SessionClosing;
656
657 if (mType == SessionType_DirectSession)
658 {
659 mConsole->uninit();
660 mConsole.setNull();
661 }
662 else
663 {
664 mRemoteMachine.setNull();
665 mRemoteConsole.setNull();
666 }
667
668 ComPtr <IProgress> progress;
669
670 if (!aFinalRelease && !aFromServer)
671 {
672 /*
673 * We trigger OnSessionEnd() only when the session closes itself using
674 * Close(). Note that if isFinalRelease = TRUE here, this means that
675 * the client process has already initialized the termination procedure
676 * without issuing Close() and the IPC channel is no more operational --
677 * so we cannot call the server's method (it will definitely fail). The
678 * server will instead simply detect the abnormal client death (since
679 * OnSessionEnd() is not called) and reset the machine state to Aborted.
680 */
681
682 /*
683 * while waiting for OnSessionEnd() to complete one of our methods
684 * can be called by the server (for example, Uninitialize(), if the
685 * direct session has initiated a closure just a bit before us) so
686 * we need to release the lock to avoid deadlocks. The state is already
687 * SessionState_SessionClosing here, so it's safe.
688 */
689 alock.leave();
690
691 LogFlowThisFunc (("Calling mControl->OnSessionEnd()...\n"));
692 HRESULT rc = mControl->OnSessionEnd (this, progress.asOutParam());
693 LogFlowThisFunc (("mControl->OnSessionEnd()=%08X\n", rc));
694
695 alock.enter();
696
697 /*
698 * If we get E_UNEXPECTED this means that the direct session has already
699 * been closed, we're just too late with our notification and nothing more
700 */
701 if (mType != SessionType_DirectSession && rc == E_UNEXPECTED)
702 rc = S_OK;
703
704 AssertComRC (rc);
705 }
706
707 mControl.setNull();
708
709 if (mType == SessionType_DirectSession)
710 {
711 releaseIPCSemaphore();
712 if (!aFinalRelease && !aFromServer)
713 {
714 /*
715 * Wait for the server to grab the semaphore and destroy the session
716 * machine (allowing us to open a new session with the same machine
717 * once this method returns)
718 */
719 Assert (!!progress);
720 if (progress)
721 progress->WaitForCompletion (-1);
722 }
723 }
724
725 mState = SessionState_SessionClosed;
726 mType = SessionType_InvalidSessionType;
727
728 /* release the VirtualBox instance as the very last step */
729 mVirtualBox.setNull();
730
731 LogFlowThisFuncLeave();
732 return S_OK;
733}
734
735/** @note To be called only from #AssignMachine() */
736HRESULT Session::grabIPCSemaphore()
737{
738 HRESULT rc = E_FAIL;
739
740 /* open the IPC semaphore based on the sessionId and try to grab it */
741 Bstr ipcId;
742 rc = mControl->GetIPCId (ipcId.asOutParam());
743 AssertComRCReturn (rc, rc);
744
745 LogFlowThisFunc (("ipcId: {%ls}\n", ipcId.raw()));
746
747#if defined(__WIN__)
748
749 /*
750 * Since Session is an MTA object, this method can be executed on
751 * any thread, and this thread will not necessarily match the thread on
752 * which close() will be called later. Therefore, we need a separate
753 * thread to hold the IPC mutex and then release it in close().
754 */
755
756 mIPCThreadSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
757 AssertMsgReturn (mIPCThreadSem,
758 ("Cannot create an event sem, err=%d", ::GetLastError()),
759 E_FAIL);
760
761 void *data [3];
762 data [0] = (void *) (BSTR) ipcId;
763 data [1] = (void *) mIPCThreadSem;
764 data [2] = 0; /* will get an output from the thread */
765
766 /* create the thread to hold the IPC mutex until signalled to release it */
767 RTTHREAD tid;
768 int vrc = RTThreadCreate (&tid, IPCMutexHolderThread, (void *) data,
769 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");
770 AssertRCReturn (vrc, E_FAIL);
771
772 /* wait until thread init is completed */
773 DWORD wrc = ::WaitForSingleObject (mIPCThreadSem, INFINITE);
774 AssertMsg (wrc == WAIT_OBJECT_0, ("Wait failed, err=%d", ::GetLastError()));
775 Assert (data [2]);
776
777 if (wrc == WAIT_OBJECT_0 && data [2])
778 {
779 /* memorize the event sem we should signal in close() */
780 mIPCSem = (HANDLE) data [2];
781 rc = S_OK;
782 }
783 else
784 {
785 ::CloseHandle (mIPCThreadSem);
786 mIPCThreadSem = NULL;
787 rc = E_FAIL;
788 }
789
790#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
791
792 Utf8Str semName = ipcId;
793 char *pszSemName = NULL;
794 RTStrUtf8ToCurrentCP (&pszSemName, semName);
795 key_t key = ::ftok (pszSemName, 0);
796 RTStrFree (pszSemName);
797
798 mIPCSem = ::semget (key, 0, 0);
799 AssertMsgReturn (mIPCSem >= 0,
800 ("Cannot open IPC semaphore, errno=%d", errno),
801 E_FAIL);
802
803 /* grab the semaphore */
804 ::sembuf sop = { 0, -1, SEM_UNDO };
805 int rv = ::semop (mIPCSem, &sop, 1);
806 AssertMsgReturn (rv == 0,
807 ("Cannot grab IPC semaphore, errno=%d", errno),
808 E_FAIL);
809
810#endif
811
812 return rc;
813}
814
815/** @note To be called only from #close() */
816void Session::releaseIPCSemaphore()
817{
818 /* release the IPC semaphore */
819#if defined(__WIN__)
820 if (mIPCSem && mIPCThreadSem)
821 {
822 /*
823 * say the thread holding the IPC mutex to release it;
824 * it will close mIPCSem handle
825 */
826 ::SetEvent (mIPCSem);
827 /* wait for the thread to finish */
828 ::WaitForSingleObject (mIPCThreadSem, INFINITE);
829 ::CloseHandle (mIPCThreadSem);
830 }
831#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
832 if (mIPCSem >= 0)
833 {
834 ::sembuf sop = { 0, 1, SEM_UNDO };
835 ::semop (mIPCSem, &sop, 1);
836 }
837#endif
838}
839
840#if defined(__WIN__)
841/** VM IPC mutex holder thread */
842DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)
843{
844 LogFlowFuncEnter();
845
846 Assert (pvUser);
847 void **data = (void **) pvUser;
848
849 BSTR sessionId = (BSTR) data [0];
850 HANDLE initDoneSem = (HANDLE) data [1];
851
852 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);
853 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%08X\n", ::GetLastError()));
854
855 if (ipcMutex)
856 {
857 /* grab the mutex */
858 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);
859 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));
860 if (wrc == WAIT_OBJECT_0)
861 {
862 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);
863 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));
864 if (finishSem)
865 {
866 data [2] = (void *) finishSem;
867 /* signal we're done with init */
868 ::SetEvent (initDoneSem);
869 /* wait until we're signaled to release the IPC mutex */
870 ::WaitForSingleObject (finishSem, INFINITE);
871 /* release the IPC mutex */
872 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));
873 BOOL success = ::ReleaseMutex (ipcMutex);
874 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));
875 ::CloseHandle (ipcMutex);
876 ::CloseHandle (finishSem);
877 }
878 }
879 }
880
881 /* signal we're done */
882 ::SetEvent (initDoneSem);
883
884 LogFlowFuncLeave();
885
886 return 0;
887}
888#endif
889
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