1/* 2 * Copyright (c) 2000-2004,2007 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// machserver - C++ shell for writing Mach 3 servers 27// 28#ifndef _H_MACHSERVER 29#define _H_MACHSERVER 30 31#include <security_utilities/mach++.h> 32#include <security_utilities/timeflow.h> 33#include <security_utilities/threading.h> 34#include <security_utilities/globalizer.h> 35#include <security_utilities/alloc.h> 36#include <security_utilities/tqueue.h> 37#include <set> 38 39namespace Security { 40namespace MachPlusPlus { 41 42 43extern "C" { 44 void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port); 45 void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port); 46 void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port); 47 void cdsa_mach_notify_send_once(mach_port_t); 48 void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t); 49}; 50 51 52// 53// Mach server object 54// 55class MachServer { 56protected: 57 class LoadThread; friend class LoadThread; 58 59 struct Allocation { 60 void *addr; 61 Allocator *allocator; 62 Allocation(void *p, Allocator &alloc) : addr(p), allocator(&alloc) { } 63 bool operator < (const Allocation &other) const 64 { return addr < other.addr || (addr == other.addr && allocator < other.allocator); } 65 }; 66 67protected: 68 struct PerThread { 69 MachServer *server; 70 set<Allocation> deferredAllocations; 71 72 PerThread() : server(NULL) { } 73 }; 74 static ModuleNexus< ThreadNexus<PerThread> > thread; 75 static PerThread &perThread() { return thread()(); } 76 77public: 78 MachServer(); 79 MachServer(const char *name); 80 MachServer(const char *name, const Bootstrap &bootstrap); 81 virtual ~MachServer(); 82 83 void run(mach_msg_size_t maxSize = 4096, mach_msg_options_t options = 0); 84 85 Time::Interval timeout() const { return workerTimeout; } 86 void timeout(Time::Interval t) { workerTimeout = t; } 87 UInt32 maxThreads() const { return maxWorkerCount; } 88 void maxThreads(UInt32 n) { maxWorkerCount = n; } 89 bool floatingThread() const { return useFloatingThread; } 90 void floatingThread(bool t) { useFloatingThread = t; } 91 92 Port primaryServicePort() const { return mServerPort; } 93 94 // listen on additional ports (dispatching to the main handler) 95 void add(Port receiver); 96 void remove(Port receiver); 97 98 // the currently active server in this thread (there can only be one) 99 static MachServer &active() 100 { assert(perThread().server); return *perThread().server; } 101 102 // request port status notifications (override virtual methods below to receive) 103 virtual void notifyIfDead(Port port, bool doNotify = true) const; 104 virtual void notifyIfUnused(Port port, bool doNotify = true) const; 105 106 // register (Allocator-derived) memory to be released after reply is sent 107 void releaseWhenDone(Allocator &alloc, void *memory); 108 109 // call if you realize that your server method will take a long time 110 void longTermActivity(); 111 112public: 113 class Timer : private ScheduleQueue<Time::Absolute>::Event { 114 friend class MachServer; 115 protected: 116 Timer(bool longTerm = false) { mLongTerm = longTerm; } 117 virtual ~Timer(); 118 119 bool longTerm() const { return mLongTerm; } 120 void longTerm(bool lt) { mLongTerm = lt; } 121 122 public: 123 virtual void action() = 0; 124 125 Time::Absolute when() const { return Event::when(); } 126 bool scheduled() const { return Event::scheduled(); } 127 128 // lifetime management hooks (default does nothing) 129 virtual void select(); 130 virtual void unselect(); 131 132 private: 133 bool mLongTerm; // long-term activity (count as worker thread) 134 }; 135 136 virtual void setTimer(Timer *timer, Time::Absolute when); 137 void setTimer(Timer *timer, Time::Interval offset) 138 { setTimer(timer, Time::now() + offset); } 139 140 virtual void clearTimer(Timer *timer); 141 142public: 143 class Handler { 144 public: 145 Handler(mach_port_t p) : mPort(p) { } 146 Handler() : mPort(MACH_PORT_NULL) { } 147 virtual ~Handler(); 148 149 mach_port_t port() const { return mPort; } 150 151 virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0; 152 153 protected: 154 void port(mach_port_t p) { assert(mPort == MACH_PORT_NULL); mPort = p; } 155 156 private: 157 mach_port_t mPort; 158 }; 159 160 class NoReplyHandler : public Handler { 161 public: 162 virtual boolean_t handle(mach_msg_header_t *in) = 0; 163 164 private: 165 boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out); 166 }; 167 168 void add(Handler &handler); 169 void remove(Handler &handler); 170 171protected: 172 // your server dispatch function 173 virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0; 174 175 // override these to receive Mach-style port notifications about your clients 176 virtual void notifyDeadName(Port port); 177 virtual void notifyPortDeleted(Port port); 178 virtual void notifyPortDestroyed(Port port); 179 virtual void notifySendOnce(Port port); 180 virtual void notifyNoSenders(Port port, mach_port_mscount_t); 181 182 // this will be called if the server wants a new thread but has hit its limit 183 virtual void threadLimitReached(UInt32 limit); 184 185 // this gets called every time the server finishes an action (any action) 186 virtual void eventDone(); 187 188 // don't mess with this unless you know what you're doing 189 Bootstrap bootstrap; // bootstrap port we registered with 190 ReceivePort mServerPort; // registered/primary server port 191 PortSet mPortSet; // joint receiver port set 192 193 mach_msg_size_t mMaxSize; // maximum message size 194 mach_msg_options_t mMsgOptions; // kernel call options 195 196 typedef set<Handler *> HandlerSet; 197 HandlerSet mHandlers; // subsidiary message port handlers 198 199protected: 200 void releaseDeferredAllocations(); 201 202protected: 203 void busy(); 204 void idle(); 205 void ensureReadyThread(); 206 207protected: 208 class LoadThread : public Thread { 209 public: 210 LoadThread(MachServer &srv) : server(srv) { } 211 212 MachServer &server; 213 214 void action(); // code implementation 215 }; 216 217 Mutex managerLock; // lock for thread-global management info below 218 set<Thread *> workers; // threads running for this server 219 UInt32 workerCount; // number of worker threads (including primary) 220 UInt32 maxWorkerCount; // administrative limit to workerCount 221 bool useFloatingThread; // keep a "floating" idle thread (instead of using longTermActivity) 222 223 UInt32 highestWorkerCount; // high water mark for workerCount 224 UInt32 idleCount; // number of threads waiting for work 225 Time::Interval workerTimeout; // seconds of idle time before a worker retires 226 Time::Absolute nextCheckTime; // next time to check for excess threads 227 UInt32 leastIdleWorkers; // max(idleCount) since last checkpoint 228 ScheduleQueue<Time::Absolute> timers; 229 230 void addThread(Thread *thread); // add thread to worker pool 231 void removeThread(Thread *thread); // remove thread from worker pool 232 bool processTimer(); // handle one due timer object, if any (return true if there was one) 233 234private: 235 static boolean_t handler(mach_msg_header_t *in, mach_msg_header_t *out); 236 void setup(const char *name); 237 void runServerThread(bool doTimeout = false); 238 239 friend void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port); 240 friend void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port); 241 friend void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port); 242 friend void cdsa_mach_notify_send_once(mach_port_t); 243 friend void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t); 244}; 245 246 247} // end namespace MachPlusPlus 248} // end namespace Security 249 250#endif //_H_MACHSERVER 251