]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/machserver.h
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / machserver.h
1 /*
2 * Copyright (c) 2000-2004,2007,2011-2012 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
39 namespace Security {
40 namespace MachPlusPlus {
41
42
43 extern "C" {
44 kern_return_t cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
45 kern_return_t cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
46 kern_return_t cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
47 kern_return_t cdsa_mach_notify_send_once(mach_port_t);
48 kern_return_t cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t);
49 };
50
51
52 //
53 // Mach server object
54 //
55 class MachServer {
56 protected:
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
67 protected:
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
77 public:
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
112 public:
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
142 public:
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
171 protected:
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
199 protected:
200 void releaseDeferredAllocations();
201
202 protected:
203 void busy();
204 void idle();
205 void ensureReadyThread();
206
207 protected:
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
234 private:
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 kern_return_t cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
240 friend kern_return_t cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
241 friend kern_return_t cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
242 friend kern_return_t cdsa_mach_notify_send_once(mach_port_t);
243 friend kern_return_t 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