VirtualBox

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

Last change on this file since 35061 was 34587, checked in by vboxsync, 14 years ago

Main: Bandwidth groups for disks (and later network)

This introduces two new interfaces. The first one named IBandwidthGroup
represents one I/O limit and can be assigned to several mediums which
share this limit (which works only for harddisk images with the disabled
host cache).
The second one IBandwdithControl manages the groups and can create new ones
and destroy them if not required anymore.

VBoxManage: commands to access the bandwidth groups

Syntax:
VBoxManage storageattach <uuid|vmname>

...
--bandwidthgroup <name>

--bandwidthgroup assigns the specified device to the given group.

VBoxManage bandwidthctl <uuid|vmname>

--name <name>
--add disk|network
--limit <megabytes per second>
--delete

The --name parameter gives the name of the bandwidth group.
--add creates a new group of the given type (only disk is implemented so far)

with the given name.

--limit sets the limit to the given amount of MB/s

Note that limit can be changed while the VM is running. The VM
will immediately pick up the new limit for the given group name.

--delete deletes the group with the given name if it isn't used anymore.

Trying to delete a still used group will result in an error.

Example:

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 20
Creates a group named Test having a 20 MB/s limit.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup Limit
Adds a new disk to the SATA controller and assigns the bandwidth group Limit to it.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup none
Removes the bandwidth limit from the disk.

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 10
Changes the limit of bandwidth group Limit to 10 MB/s. If the VM is running the limit will be picked up
immediately.

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