VirtualBox

Changeset 104205 in vbox for trunk/src/VBox/Main


Ignore:
Timestamp:
Apr 5, 2024 5:38:51 PM (10 months ago)
Author:
vboxsync
Message:

Main/webservice/vboxweb.cpp: Use a different approach of socket queueing which is closer to the one used in the examples. Easier to understand than the old one. Also eliminate the code using select() again, because with current gSOAP the service shutdown time is almost as good. The extra complexity isn't worth it for an improvement under a second.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/webservice/vboxweb.cpp

    r99739 r104205  
    140140static const char       *g_pcszBindToHost = NULL;       // host; NULL = localhost
    141141static unsigned int     g_uBindToPort = 18083;          // port
    142 static unsigned int     g_uBacklog = 100;               // backlog = max queue size for requests
     142static unsigned int     g_uBacklog = 100;               // backlog = max queue size for connections
    143143
    144144#ifdef WITH_OPENSSL
     
    307307
    308308            case 'k':
    309                 pcszDescr = "Maximum number of requests before a socket will be closed (100).";
     309                pcszDescr = "Maximum number of requests before a connection will be closed (100).";
    310310                break;
    311311
     
    362362     * @param u Thread number. (So we can count from 1 and be readable.)
    363363     * @param q SoapQ instance which has the queue to process.
    364      * @param soap struct soap instance from main() which we copy here.
    365364     */
    366365    SoapThread(size_t u,
    367                SoapQ &q,
    368                const struct soap *soap)
     366               SoapQ &q)
    369367        : m_u(u),
    370           m_strThread(com::Utf8StrFmt("SQW%02d", m_u)),
    371368          m_pQ(&q)
    372369    {
    373         // make a copy of the soap struct for the new thread
    374         m_soap = soap_copy(soap);
    375         m_soap->fget = fnHttpGet;
    376 
    377         /* The soap.max_keep_alive value can be set to the maximum keep-alive calls allowed,
    378          * which is important to avoid a client from holding a thread indefinitely.
    379          * http://www.cs.fsu.edu/~engelen/soapdoc2.html#sec:keepalive
    380          *
    381          * Strings with 8-bit content can hold ASCII (default) or UTF8. The latter is
    382          * possible by enabling the SOAP_C_UTFSTRING flag.
    383          */
    384         soap_set_omode(m_soap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING);
    385         soap_set_imode(m_soap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING);
    386         m_soap->max_keep_alive = g_cMaxKeepAlive;
    387 
     370         m_strThread = com::Utf8StrFmt("SQW%02d", m_u);
    388371        int vrc = RTThreadCreate(&m_pThread,
    389372                                 fntWrapper,
     
    432415    com::Utf8Str    m_strThread;    // thread name ("SoapQWrkXX")
    433416    SoapQ           *m_pQ;          // the single SOAP queue that all the threads service
    434     struct soap     *m_soap;        // copy of the soap structure for this thread (from soap_copy())
    435417    RTTHREAD        m_pThread;      // IPRT thread struct for this thread
    436418};
     
    447429    /**
    448430     * Constructor. Creates the soap queue.
    449      * @param pSoap
    450431     */
    451     SoapQ(const struct soap *pSoap)
    452         : m_soap(pSoap),
    453           m_mutex(util::LOCKCLASS_OBJECTSTATE),     // lowest lock order, no other may be held while this is held
     432    SoapQ()
     433        : m_mutex(util::LOCKCLASS_OBJECTSTATE),     // lowest lock order, no other may be held while this is held
    454434          m_cIdleThreads(0)
    455435    {
     
    475455
    476456        RTSemEventMultiDestroy(m_event);
     457
     458        while (!m_llSocketsQ.empty())
     459        {
     460            struct soap *pSoap = m_llSocketsQ.front();
     461            m_llSocketsQ.pop_front();
     462
     463            if (pSoap == NULL || !soap_valid_socket(pSoap->socket))
     464                continue;
     465
     466            soap_destroy(pSoap); // clean up class instances
     467            soap_end(pSoap); // clean up everything and close socket
     468            soap_free(pSoap); // free soap connection
     469        }
    477470    }
    478471
    479472    /**
    480      * Adds the given socket to the SOAP queue and posts the
     473     * Adds the given soap connection to the SOAP queue and posts the
    481474     * member event sem to wake up the workers. Called on the main thread
    482      * whenever a socket has work to do. Creates a new SOAP thread on the
     475     * whenever a connection comes in. Creates a new SOAP thread on the
    483476     * first call or when all existing threads are busy.
    484      * @param s Socket from soap_accept() which has work to do.
     477     * @param pSoap connection
    485478     */
    486     size_t add(SOAP_SOCKET s)
     479    size_t add(const struct soap *pSoap)
    487480    {
    488481        size_t cItems;
     
    497490        {
    498491            SoapThread *pst = new SoapThread(m_llAllThreads.size() + 1,
    499                                              *this,
    500                                              m_soap);
     492                                             *this);
    501493            m_llAllThreads.push_back(pst);
    502494            util::AutoWriteLock thrLock(g_pThreadsLockHandle COMMA_LOCKVAL_SRC_POS);
     
    505497        }
    506498
    507         // enqueue the socket of this connection and post eventsem so that
    508         // one of the threads (possibly the one just created) can pick it up
    509         m_llSocketsQ.push_back(s);
     499        // enqueue this connection and post eventsem so that one of the threads
     500        // (possibly the one just created) can pick it up
     501        m_llSocketsQ.push_back(soap_copy(pSoap));
    510502        cItems = m_llSocketsQ.size();
    511503        qlock.release();
     
    519511    /**
    520512     * Blocks the current thread until work comes in; then returns
    521      * the SOAP socket which has work to do. This reduces m_cIdleThreads
     513     * the SOAP connection which has work to do. This reduces m_cIdleThreads
    522514     * by one, and the caller MUST call done() when it's done processing.
    523515     * Called from the worker threads.
     
    526518     * @return
    527519     */
    528     SOAP_SOCKET get(size_t &cIdleThreads, size_t &cThreads)
     520    struct soap *get(size_t &cIdleThreads, size_t &cThreads)
    529521    {
    530522        while (g_fKeepRunning)
     
    539531            if (!m_llSocketsQ.empty())
    540532            {
    541                 SOAP_SOCKET socket = m_llSocketsQ.front();
     533                struct soap *pSoap = m_llSocketsQ.front();
    542534                m_llSocketsQ.pop_front();
    543535                cIdleThreads = --m_cIdleThreads;
     
    552544                qlock.release();
    553545
    554                 return socket;
     546                return pSoap;
    555547            }
    556548
    557549            // nothing to do: keep looping
    558550        }
    559         return SOAP_INVALID_SOCKET;
     551        return NULL;
    560552    }
    561553
     
    572564    /**
    573565     * To be called by a worker thread when signing off, i.e. no longer
    574      * willing to process requests.
     566     * willing to process SOAP connections.
    575567     */
    576568    void signoff(SoapThread *th)
     
    588580    }
    589581
    590     const struct soap       *m_soap;            // soap structure created by main(), passed to constructor
    591 
    592582    util::WriteLockHandle   m_mutex;
    593583    RTSEMEVENTMULTI         m_event;            // posted by add(), blocked on by get()
     
    597587
    598588    // A std::list abused as a queue; this contains the actual jobs to do,
    599     // each int being a socket from soap_accept()
    600     std::list<SOAP_SOCKET>  m_llSocketsQ;
     589    // each entry being a connection from soap_accept() passed through SoapQ::add.
     590    std::list<struct soap *>  m_llSocketsQ;
    601591};
    602592
     
    604594 * Thread function for each of the SOAP queue worker threads. This keeps
    605595 * running, blocks on the event semaphore in SoapThread.SoapQ and picks
    606  * up a socket from the queue therein, which has been put there by
    607  * beginProcessing().
     596 * up a connection from the queue therein, which has been put there by
     597 * SoapQ::add().
    608598 */
    609599void SoapThread::process()
     
    613603    while (g_fKeepRunning)
    614604    {
    615         // wait for a socket to arrive on the queue
     605        // wait for a job to arrive on the queue
    616606        size_t cIdleThreads = 0, cThreads = 0;
    617         m_soap->socket = m_pQ->get(cIdleThreads, cThreads);
    618 
    619         if (!soap_valid_socket(m_soap->socket))
     607        struct soap *pSoap = m_pQ->get(cIdleThreads, cThreads);
     608
     609        if (pSoap == NULL || !soap_valid_socket(pSoap->socket))
    620610            continue;
    621611
     612        pSoap->fget = fnHttpGet;
     613
     614        /* The soap.max_keep_alive value can be set to the maximum keep-alive calls allowed,
     615         * which is important to avoid a client from holding a thread indefinitely.
     616         * http://www.cs.fsu.edu/~engelen/soapdoc2.html#sec:keepalive
     617         *
     618         * Strings with 8-bit content can hold ASCII (default) or UTF8. The latter is
     619         * possible by enabling the SOAP_C_UTFSTRING flag.
     620         */
     621        soap_set_omode(pSoap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING);
     622        soap_set_imode(pSoap, SOAP_IO_KEEPALIVE | SOAP_C_UTFSTRING);
     623        pSoap->max_keep_alive = g_cMaxKeepAlive;
     624
    622625        LogRel(("Processing connection from IP=%RTnaipv4 socket=%d (%d out of %d threads idle)\n",
    623                 RT_H2N_U32(m_soap->ip), m_soap->socket, cIdleThreads, cThreads));
     626                RT_H2N_U32(pSoap->ip), pSoap->socket, cIdleThreads, cThreads));
    624627
    625628        // Ensure that we don't get stuck indefinitely for connections using
    626629        // keepalive, otherwise stale connections tie up worker threads.
    627         m_soap->send_timeout = 60;
    628         m_soap->recv_timeout = 60;
     630        pSoap->send_timeout = 60;
     631        pSoap->recv_timeout = 60;
    629632        // Limit the maximum SOAP request size to a generous amount, just to
    630633        // be on the safe side (SOAP is quite wordy when representing arrays,
    631634        // and some API uses need to deal with large arrays). Good that binary
    632635        // data is no longer represented by byte arrays...
    633         m_soap->recv_maxlength = _16M;
     636        pSoap->recv_maxlength = _16M;
    634637        // process the request; this goes into the COM code in methodmaps.cpp
     638
    635639        do {
    636640#ifdef WITH_OPENSSL
    637             if (g_fSSL && soap_ssl_accept(m_soap))
     641            if (g_fSSL && soap_ssl_accept(pSoap))
    638642            {
    639                 WebLogSoapError(m_soap);
     643                WebLogSoapError(pSoap);
    640644                break;
    641645            }
    642646#endif /* WITH_OPENSSL */
    643             soap_serve(m_soap);
     647            soap_serve(pSoap);
    644648        } while (0);
    645649
    646         soap_destroy(m_soap); // clean up class instances
    647         soap_end(m_soap); // clean up everything and close socket
     650        soap_destroy(pSoap); // clean up class instances
     651        soap_end(pSoap); // clean up everything and close connection
     652        soap_free(pSoap); // free soap connection
    648653
    649654        // tell the queue we're idle again
     
    909914#endif /* WITH_OPENSSL */
    910915
     916    soap.accept_timeout = 60;
    911917    soap.bind_flags |= SO_REUSEADDR;
    912918            // avoid EADDRINUSE on bind()
     
    916922                  g_pcszBindToHost ? g_pcszBindToHost : "localhost",    // safe default host
    917923                  g_uBindToPort,    // port
    918                   g_uBacklog);      // backlog = max queue size for requests
     924                  g_uBacklog);      // backlog = max queue size for connections
    919925    if (m == SOAP_INVALID_SOCKET)
    920926        WebLogSoapError(&soap);
     
    931937
    932938        // initialize thread queue, mutex and eventsem
    933         g_pSoapQ = new SoapQ(&soap);
     939        g_pSoapQ = new SoapQ();
    934940
    935941        uint64_t cAccepted = 1;
    936942        while (g_fKeepRunning)
    937943        {
    938             struct timeval timeout;
    939             fd_set ReadFds, WriteFds, XcptFds;
    940             int rv;
    941             for (;;)
    942             {
    943                 timeout.tv_sec = 60;
    944                 timeout.tv_usec = 0;
    945                 FD_ZERO(&ReadFds);
    946                 FD_SET(soap.master, &ReadFds);
    947                 FD_ZERO(&WriteFds);
    948                 FD_SET(soap.master, &WriteFds);
    949                 FD_ZERO(&XcptFds);
    950                 FD_SET(soap.master, &XcptFds);
    951                 rv = select((int)soap.master + 1, &ReadFds, &WriteFds, &XcptFds, &timeout);
    952                 if (rv > 0)
    953                     break; // work is waiting
    954                 if (rv == 0)
    955                     continue; // timeout, not necessary to bother gsoap
    956                 // r < 0, errno
    957 #if GSOAP_VERSION >= 208103
    958                 if (soap_socket_errno == SOAP_EINTR)
    959 #else
    960                 if (soap_socket_errno(soap.master) == SOAP_EINTR)
    961 #endif
    962                     rv = 0; // re-check if we should terminate
    963                 break;
    964             }
    965             if (rv == 0)
    966                 continue;
    967 
    968             // call gSOAP to handle incoming SOAP connection
    969             soap.accept_timeout = -1; // 1usec timeout, actual waiting is above
    970944            s = soap_accept(&soap);
    971945            if (!soap_valid_socket(s))
    972946            {
    973                 if (soap.errnum)
     947                if (soap.errnum != SOAP_EINTR)
    974948                    WebLogSoapError(&soap);
    975949                continue;
    976950            }
    977951
    978             // add the socket to the queue and tell worker threads to
     952            // add the connection to the queue and tell worker threads to
    979953            // pick up the job
    980             size_t cItemsOnQ = g_pSoapQ->add(s);
    981             LogRel(("Request %llu on socket %d queued for processing (%d items on Q)\n", cAccepted, s, cItemsOnQ));
     954            size_t cItemsOnQ = g_pSoapQ->add(&soap);
     955            LogRel(("Connection %llu on socket %d queued for processing (%d items on Q)\n", cAccepted, s, cItemsOnQ));
    982956            cAccepted++;
    983957        }
     
    986960        g_pSoapQ = NULL;
    987961
    988         LogRel(("ending SOAP request handling\n"));
    989 
    990         delete g_pSoapQ;
    991         g_pSoapQ = NULL;
     962        LogRel(("Ending SOAP connection handling\n"));
    992963
    993964    }
     
    1002973/**
    1003974 * Thread function for the "queue pumper" thread started from main(). This implements
    1004  * the loop that takes SOAP calls from HTTP and serves them by handing sockets to the
     975 * the loop that takes SOAP calls from HTTP and serves them by handing connections to the
    1005976 * SOAP queue worker threads.
    1006977 */
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