VirtualBox

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

Last change on this file since 8057 was 8057, checked in by vboxsync, 16 years ago

Main: Fixed another self-deadlock (requesting write before releasing read).

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