VirtualBox

Changeset 2331 in vbox


Ignore:
Timestamp:
Apr 24, 2007 4:21:52 PM (18 years ago)
Author:
vboxsync
Message:

Main: Implemented server shutdown delay on VirtualBox instantiation failure (to let front-ends completely fetch error info from a server-side IVirtualBoxErrorInfo object).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/linux/server.cpp

    r2057 r2331  
    202202static PRBool volatile gKeepRunning = PR_TRUE;
    203203
     204/////////////////////////////////////////////////////////////////////////////
     205
     206/**
     207 * Simple but smart PLEvent wrapper.
     208 *
     209 * @note Instances must be always created with <tt>operator new</tt>!
     210 */
     211class MyEvent
     212{
     213public:
     214
     215    MyEvent()
     216    {
     217        mEv.that = NULL;
     218    };
     219
     220    /**
     221     * Posts this event to the given message queue. This method may only be
     222     * called once. @note On success, the event will be deleted automatically
     223     * after it is delivered and handled. On failure, the event will delete
     224     * itself before this method returns! The caller must not delete it in
     225     * either case.
     226     */
     227    nsresult postTo (nsIEventQueue *aEventQ)
     228    {
     229        AssertReturn (mEv.that == NULL, NS_ERROR_FAILURE);
     230        AssertReturn (aEventQ, NS_ERROR_FAILURE);
     231        nsresult rv = aEventQ->InitEvent (&mEv.e, NULL,
     232                                          eventHandler, eventDestructor);
     233        if (NS_SUCCEEDED (rv))
     234        {
     235            mEv.that = this;
     236            rv = aEventQ->PostEvent (&mEv.e);
     237            if (NS_SUCCEEDED (rv))
     238                return rv;
     239        }
     240        delete this;
     241        return rv;
     242    }
     243
     244    virtual void *handler() = 0;
     245
     246private:
     247
     248    struct Ev
     249    {
     250        PLEvent e;
     251        MyEvent *that;
     252    } mEv;
     253
     254    static void *PR_CALLBACK eventHandler (PLEvent *self)
     255    {
     256        return reinterpret_cast <Ev *> (self)->that->handler();
     257    }
     258
     259    static void PR_CALLBACK eventDestructor (PLEvent *self)
     260    {
     261        delete reinterpret_cast <Ev *> (self)->that;
     262    }
     263};
     264
    204265////////////////////////////////////////////////////////////////////////////////
    205266
     
    222283        LogFlowFunc (("VirtualBox object deleted.\n"));
    223284        printf ("Informational: VirtualBox object deleted.\n");
    224 
    225         /* Instruct the main event loop to terminate. Note that it's enough
    226          * to set gKeepRunning to false because we are on the main thread
    227          * already (i.e. no need to post events there). */
    228         if (gAutoShutdown)
    229             gKeepRunning = PR_FALSE;
    230285    }
    231286
     
    252307            if (sTimer != NULL)
    253308            {
    254                 LogFlowFunc (("Last VirtualBox instance was released, "
    255                               "scheduling server shutdown in %d ms...\n",
     309                LogFlowFunc (("Last VirtualBox instance was released.\n"));
     310                LogFlowFunc (("Scheduling server shutdown in %d ms...\n",
    256311                              VBoxSVC_ShutdownDelay));
     312
     313                /* make sure the previous timer (if any) is stopped;
     314                 * otherwise RTTimerStart() will definitely fail. */
     315                RTTimerStop (sTimer);
    257316
    258317                int vrc = RTTimerStart (sTimer, uint64_t (VBoxSVC_ShutdownDelay) * 1000000);
     
    303362    }
    304363
    305     /* Returns the current value of the reference counter. */
    306     nsrefcnt GetRefCount()
    307     {
    308         /* we don't use our own Release() to avoid its "side effect" */
    309         nsrefcnt count = VirtualBox::AddRef();
    310         count = VirtualBox::Release();
    311         return count;
    312     }
    313 
    314     /* called on the main thread */
    315     static void *PR_CALLBACK DestructEventHandler (PLEvent* self)
    316     {
    317         Assert (RTCritSectIsInitialized (&sLock));
    318 
    319         /* stop accepting GetInstance() requests during possible destruction */
    320         RTCritSectEnter (&sLock);
    321 
    322         Assert (sInstance);
    323 
    324         nsrefcnt count = sInstance->GetRefCount();
    325         AssertMsg (count >= 1, ("count=%d\n", count));
    326 
    327         if (count > 1)
    328         {
    329             /* This case is very unlikely because we stop the timer when a new
    330              * client connects after the instance was scheduled for
    331              * destruction, but it's still possible. This is the only reason
    332              * for the above GetRefCount() btw.  */
    333             LogFlowFunc (("Destruction is canceled (refcnt=%d).\n", count));
    334         }
    335         else
    336         {
    337             /* release the last (first) reference we added in GetInstance()
    338              * (this must call the destructor) */
    339             nsrefcnt count = sInstance->Release();
    340             AssertMsg (count == 0, ("count=%d\n", count));
    341             NOREF(count);
    342         }
    343 
    344         RTCritSectLeave (&sLock);
    345 
    346         return 0;
    347     }
    348 
    349     static void PR_CALLBACK DestructEventDestructor (PLEvent* self)
    350     {
    351         delete self;
    352     }
     364    class MaybeQuitEvent : public MyEvent
     365    {
     366        /* called on the main thread */
     367        void *handler()
     368        {
     369            LogFlowFunc (("\n"));
     370
     371            Assert (RTCritSectIsInitialized (&sLock));
     372
     373            /* stop accepting GetInstance() requests on other threads during
     374             * possible destruction */
     375            RTCritSectEnter (&sLock);
     376
     377            nsrefcnt count = 0;
     378
     379            /* sInstance is NULL here if it was deleted immediately after
     380             * creation due to initialization error. See GetInstance(). */
     381            if (sInstance != NULL)
     382            {
     383                /* Release the guard reference added in GetInstance() */
     384                count = sInstance->Release();
     385            }
     386
     387            if (count == 0)
     388            {
     389                if (gAutoShutdown)
     390                {
     391                    Assert (sInstance == NULL);
     392                    LogFlowFunc (("Terminating the server process...\n"));
     393                    /* make it leave the event loop */
     394                    gKeepRunning = PR_FALSE;
     395                }
     396            }
     397            else
     398            {
     399                /* This condition is quite rare: a new client will have to
     400                 * connect after this event has been posted to the main queue
     401                 * but before it started to process it. */
     402                LogFlowFunc (("Destruction is canceled (refcnt=%d).\n", count));
     403            }
     404
     405            RTCritSectLeave (&sLock);
     406
     407            return NULL;
     408        }
     409    };
    353410
    354411    static void ShutdownTimer (PRTTIMER pTimer, void *pvUser)
     
    363420        AssertReturnVoid (gEventQ);
    364421
    365         /* post a destruction event to the main thread to safely release the
    366          * extra reference added in VirtualBoxClassFactory::GetInstance() */
    367 
    368         LogFlowFunc (("Posting VirtualBox destruction & shtutdown event...\n"));
    369 
    370         PLEvent *ev = new PLEvent;
    371         gEventQ->InitEvent (ev, NULL, DestructEventHandler,
    372                             DestructEventDestructor);
    373         nsresult rv = gEventQ->PostEvent (ev);
    374         if (NS_FAILED (rv))
    375         {
    376             /* this means we've been already stopped (for example
    377              * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
    378              * will do the job. */
    379             PL_DestroyEvent (ev);
    380         }
     422        /* post a quit event to the main queue */
     423        MaybeQuitEvent *ev = new MaybeQuitEvent();
     424        nsresult rv = ev->postTo (gEventQ);
     425        NOREF (rv);
     426
     427        /* A failure above means we've been already stopped (for example
     428         * by Ctrl-C). FactoryDestructor() (NS_ShutdownXPCOM())
     429         * will do the job. Nothing to do. */
    381430    }
    382431
     
    414463             * or the client has terminated abnormally w/o releasing its
    415464             * VirtualBox instance (so NS_ShutdownXPCOM() is doing a cleanup).
    416              * Release the extra reference we added in GetInstance(). */
     465             * Release the guard reference we added in GetInstance(). */
    417466            sInstance->Release();
    418467        }
     
    444493                if (NS_FAILED (rv))
    445494                {
    446                     /* on failure diring VirtualBox initialization, delete it
    447                      * immediately on the current thread, ignoring the reference
    448                      * count (VirtualBox should be aware of that meaning that it
    449                      * has already completely unintialized itself in this
    450                      * case) */
    451                     LogFlowFunc (("VirtualBox creation failed "
    452                                   "(rc=%08X), deleting immediately...\n", rv));
    453                     delete sInstance;
    454                     sInstance = 0;
     495                    /* On failure diring VirtualBox initialization, delete it
     496                     * immediately on the current thread by releasing all
     497                     * references in order to properly schedule the server
     498                     * shutdown. Since the object is fully deleted here, there
     499                     * is a chance to fix the error and request a new
     500                     * instantiation before the server terminates. However,
     501                     * the main reason to maintain the shoutdown delay on
     502                     * failure is to let the front-end completely fetch error
     503                     * info from a server-side IVirtualBoxErrorInfo object. */
     504                    sInstance->Release();
     505                    sInstance->Release();
     506                    Assert (sInstance == 0);
     507                }
     508                else
     509                {
     510                    /* On success, make sure the previous timer is stopped to
     511                     * cancel a scheduled server termination (if any). */
     512                    RTTimerStop (sTimer);
    455513                }
    456514            }
     
    468526            if (count == 2)
    469527            {
    470                 LogFlowFunc (("Another client has requested "
    471                               "a reference of VirtualBox scheduled for destruction, "
     528                LogFlowFunc (("Another client has requested a reference to VirtualBox, "
    472529                              "canceling detruction...\n"));
    473530               
     
    821878static char *pszPidFile = NULL;
    822879
    823 void* PR_CALLBACK quitEventHandler (PLEvent* self) { gKeepRunning = PR_FALSE; return 0; }
    824 void PR_CALLBACK quitEventDestructor (PLEvent* self) { delete self; }
     880class ForceQuitEvent : public MyEvent
     881{
     882    void *handler()
     883    {
     884        LogFlowFunc (("\n"));
     885
     886        gKeepRunning = PR_FALSE;
     887
     888        if (pszPidFile)
     889            RTFileDelete(pszPidFile);
     890
     891        return NULL;
     892    }
     893};
    825894
    826895static void signal_handler (int sig)
     
    829898    {
    830899        /* post a quit event to the queue */
    831         PLEvent *ev = new PLEvent;
    832         gEventQ->InitEvent (ev, NULL, quitEventHandler, quitEventDestructor);
    833         gEventQ->PostEvent (ev);
    834     }
    835     if (pszPidFile)
    836     {
    837         RTFileDelete(pszPidFile);
    838     }
    839 };
     900        ForceQuitEvent *ev = new ForceQuitEvent();
     901        ev->postTo (gEventQ);
     902    }
     903}
    840904
    841905#if defined(USE_BACKTRACE)
Note: See TracChangeset for help on using the changeset viewer.

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