Logo Search packages:      
Sourcecode: coreutils version File versions  Download package

machserver.h

/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// machserver - C++ shell for writing Mach 3 servers
//
#ifndef _H_MACHSERVER
#define _H_MACHSERVER

#include <security_utilities/mach++.h>
#include <security_utilities/timeflow.h>
#include <security_utilities/threading.h>
#include <security_utilities/globalizer.h>
#include <security_utilities/alloc.h>
#include <security_utilities/tqueue.h>
#include <set>

namespace Security {
namespace MachPlusPlus {


extern "C" {
      void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
      void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
      void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
      void cdsa_mach_notify_send_once(mach_port_t);
      void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t);
};


//
// Mach server object
//
class MachServer {
protected:
      class LoadThread; friend class LoadThread;
      
      struct Allocation {
            void *addr;
            Allocator *allocator;
            Allocation(void *p, Allocator &alloc) : addr(p), allocator(&alloc) { }
            bool operator < (const Allocation &other) const
            { return addr < other.addr || (addr == other.addr && allocator < other.allocator); }
      };
    
protected:
    struct PerThread {
        MachServer *server;
        set<Allocation> deferredAllocations;

        PerThread() : server(NULL) { }
    };
    static ModuleNexus< ThreadNexus<PerThread> > thread;
    static PerThread &perThread()   { return thread()(); }
    
public:
      MachServer();
    MachServer(const char *name);
      MachServer(const char *name, const Bootstrap &bootstrap);
      virtual ~MachServer();
      
      void run(size_t maxSize = 4096, mach_msg_options_t options = 0);
      
      Time::Interval timeout() const { return workerTimeout; }
      void timeout(Time::Interval t)      { workerTimeout = t; }
      UInt32 maxThreads() const           { return maxWorkerCount; }
      void maxThreads(UInt32 n)           { maxWorkerCount = n; }
      
      Port primaryServicePort() const     { return mServerPort; }
      
      // listen on additional ports (dispatching to the main handler)
      void add(Port receiver);
      void remove(Port receiver);

      // the currently active server in this thread (there can only be one)
      static MachServer &active()
      { assert(perThread().server); return *perThread().server; }
      
      // request port status notifications (override virtual methods below to receive)
      virtual void notifyIfDead(Port port, bool doNotify = true) const;
      virtual void notifyIfUnused(Port port, bool doNotify = true) const;

      // register (Allocator-derived) memory to be released after reply is sent
      void releaseWhenDone(Allocator &alloc, void *memory);
      
      // call if you realize that your server method will take a long time
      void longTermActivity();

public:
      class Timer : private ScheduleQueue<Time::Absolute>::Event {
            friend class MachServer;
      protected:
            Timer(bool longTerm = false) { mLongTerm = longTerm; }
            virtual ~Timer();

            bool longTerm() const         { return mLongTerm; }
            void longTerm(bool lt)        { mLongTerm = lt; }
      
      public:
            virtual void action() = 0;
            
            Time::Absolute when() const   { return Event::when(); }
            bool scheduled() const        { return Event::scheduled(); }
      
      private:
            bool mLongTerm;                     // long-term activity (count as worker thread)
      };
      
      virtual void setTimer(Timer *timer, Time::Absolute when);
      void setTimer(Timer *timer, Time::Interval offset)
      { setTimer(timer, Time::now() + offset); }

      virtual void clearTimer(Timer *timer);

public:
    class Handler {
    public:
        Handler(mach_port_t p) : mPort(p) { }
        Handler() : mPort(MACH_PORT_NULL) { }
            virtual ~Handler();
        
        mach_port_t port() const    { return mPort; }
        
        virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0;
        
    protected:
        void port(mach_port_t p)    { assert(mPort == MACH_PORT_NULL); mPort = p; }

    private:
        mach_port_t mPort;
    };
    
    class NoReplyHandler : public Handler {
    public:
        virtual boolean_t handle(mach_msg_header_t *in) = 0;

    private:
        boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out);
    };
    
    void add(Handler &handler);
    void remove(Handler &handler);
    
protected:
      // your server dispatch function
      virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0;
      
      // override these to receive Mach-style port notifications about your clients
      virtual void notifyDeadName(Port port);
      virtual void notifyPortDeleted(Port port);
      virtual void notifyPortDestroyed(Port port);
      virtual void notifySendOnce(Port port);
      virtual void notifyNoSenders(Port port, mach_port_mscount_t);
      
      // this will be called if the server wants a new thread but has hit its limit
      virtual void threadLimitReached(UInt32 limit);

      // don't mess with this unless you know what you're doing
    Bootstrap bootstrap;                  // bootstrap port we registered with
      ReceivePort mServerPort;            // registered/primary server port
    PortSet mPortSet;                     // joint receiver port set
      
      size_t mMaxSize;                    // maximum message size
      mach_msg_options_t mMsgOptions;     // kernel call options
    
    typedef set<Handler *> HandlerSet;
    HandlerSet mHandlers;                 // subsidiary message port handlers

protected:  
      void releaseDeferredAllocations();

protected:
      void busy() { StLock<Mutex> _(managerLock); idleCount--; }
      void idle() { StLock<Mutex> _(managerLock); idleCount++; }

protected:
      class LoadThread : public Thread {
      public:
            LoadThread(MachServer &srv) : server(srv) { }
            
            MachServer &server;
            
            void action();          // code implementation
      };
      
      Mutex managerLock;            // lock for thread-global management info below
      set<Thread *> workers;  // threads running for this server
      UInt32 workerCount;           // number of worker threads (including primary)
      UInt32 maxWorkerCount;  // administrative limit to workerCount
      UInt32 highestWorkerCount; // high water mark for workerCount
      UInt32 idleCount;       // number of threads waiting for work
      Time::Interval workerTimeout; // seconds of idle time before a worker retires
      Time::Absolute nextCheckTime; // next time to check for excess threads
      UInt32 leastIdleWorkers; // max(idleCount) since last checkpoint
      ScheduleQueue<Time::Absolute> timers;

      void addThread(Thread *thread); // add thread to worker pool
      void removeThread(Thread *thread); // remove thread from worker pool
      bool processTimer();    // handle one due timer object, if any

private:
      static boolean_t handler(mach_msg_header_t *in, mach_msg_header_t *out);
    void setup(const char *name);
      void runServerThread(bool doTimeout = false);
      
      friend void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
      friend void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
      friend void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
      friend void cdsa_mach_notify_send_once(mach_port_t);
      friend void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t);
};


} // end namespace MachPlusPlus
} // end namespace Security

#endif //_H_MACHSERVER

Generated by  Doxygen 1.6.0   Back to index