2 * Copyright (c) 2000-2004,2007,2011-2012 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // machserver - C++ shell for writing Mach 3 servers
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>
40 namespace MachPlusPlus
{
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
);
57 class LoadThread
; friend class LoadThread
;
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
); }
70 set
<Allocation
> deferredAllocations
;
72 PerThread() : server(NULL
) { }
74 static ModuleNexus
< ThreadNexus
<PerThread
> > thread
;
75 static PerThread
&perThread() { return thread()(); }
79 MachServer(const char *name
);
80 MachServer(const char *name
, const Bootstrap
&bootstrap
);
81 virtual ~MachServer();
83 void run(mach_msg_size_t maxSize
= 4096, mach_msg_options_t options
= 0);
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
; }
92 Port
primaryServicePort() const { return mServerPort
; }
94 // listen on additional ports (dispatching to the main handler)
95 void add(Port receiver
);
96 void remove(Port receiver
);
98 // the currently active server in this thread (there can only be one)
99 static MachServer
&active()
100 { assert(perThread().server
); return *perThread().server
; }
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;
106 // register (Allocator-derived) memory to be released after reply is sent
107 void releaseWhenDone(Allocator
&alloc
, void *memory
);
109 // call if you realize that your server method will take a long time
110 void longTermActivity();
113 class Timer
: private ScheduleQueue
<Time::Absolute
>::Event
{
114 friend class MachServer
;
116 Timer(bool longTerm
= false) { mLongTerm
= longTerm
; }
119 bool longTerm() const { return mLongTerm
; }
120 void longTerm(bool lt
) { mLongTerm
= lt
; }
123 virtual void action() = 0;
125 Time::Absolute
when() const { return Event::when(); }
126 bool scheduled() const { return Event::scheduled(); }
128 // lifetime management hooks (default does nothing)
129 virtual void select();
130 virtual void unselect();
133 bool mLongTerm
; // long-term activity (count as worker thread)
136 virtual void setTimer(Timer
*timer
, Time::Absolute when
);
137 void setTimer(Timer
*timer
, Time::Interval offset
)
138 { setTimer(timer
, Time::now() + offset
); }
140 virtual void clearTimer(Timer
*timer
);
145 Handler(mach_port_t p
) : mPort(p
) { }
146 Handler() : mPort(MACH_PORT_NULL
) { }
149 mach_port_t
port() const { return mPort
; }
151 virtual boolean_t
handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
) = 0;
154 void port(mach_port_t p
) { assert(mPort
== MACH_PORT_NULL
); mPort
= p
; }
160 class NoReplyHandler
: public Handler
{
162 virtual boolean_t
handle(mach_msg_header_t
*in
) = 0;
165 boolean_t
handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
);
168 void add(Handler
&handler
);
169 void remove(Handler
&handler
);
172 // your server dispatch function
173 virtual boolean_t
handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
) = 0;
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
);
182 // this will be called if the server wants a new thread but has hit its limit
183 virtual void threadLimitReached(UInt32 limit
);
185 // this gets called every time the server finishes an action (any action)
186 virtual void eventDone();
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
193 mach_msg_size_t mMaxSize
; // maximum message size
194 mach_msg_options_t mMsgOptions
; // kernel call options
196 typedef set
<Handler
*> HandlerSet
;
197 HandlerSet mHandlers
; // subsidiary message port handlers
200 void releaseDeferredAllocations();
205 void ensureReadyThread();
208 class LoadThread
: public Thread
{
210 LoadThread(MachServer
&srv
) : server(srv
) { }
214 void action(); // code implementation
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)
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
;
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)
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);
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
);
247 } // end namespace MachPlusPlus
248 } // end namespace Security
250 #endif //_H_MACHSERVER