void MachServer::setup(const char *name)
{
- debug("machsrv", "%p preparing service for \"%s\"", this, name);
+ secdebug("machsrv", "%p preparing service for \"%s\"", this, name);
workerTimeout = 60 * 2; // 2 minutes default timeout
maxWorkerCount = 100; // sanity check limit
{
// The ReceivePort members will clean themselves up.
// The bootstrap server will clear us from its map when our receive port dies.
- debug("machsrv", "%p destroyed", this);
+ secdebug("machsrv", "%p destroyed", this);
}
//
-// Utility access
+// Add and remove extra listening ports.
+// Messages directed to those ports are dispatched through the main handler.
+// To get automatic call-out to another handler, use the Handler class.
//
-void MachServer::notifyIfDead(Port port) const
+void MachServer::add(Port receiver)
{
- port.requestNotify(mServerPort, MACH_NOTIFY_DEAD_NAME, true);
+ secdebug("machsrv", "adding port %d to primary dispatch", receiver.port());
+ mPortSet += receiver;
+}
+
+void MachServer::remove(Port receiver)
+{
+ secdebug("machsrv", "removing port %d from primary dispatch", receiver.port());
+ mPortSet -= receiver;
+}
+
+
+//
+// Register for mach port notifications
+//
+void MachServer::notifyIfDead(Port port, bool doNotify) const
+{
+ if (doNotify)
+ port.requestNotify(mServerPort, MACH_NOTIFY_DEAD_NAME, true);
+ else
+ port.cancelNotify(MACH_NOTIFY_DEAD_NAME);
+}
+
+void MachServer::notifyIfUnused(Port port, bool doNotify) const
+{
+ if (doNotify)
+ port.requestNotify(port, MACH_NOTIFY_NO_SENDERS, true);
+ else
+ port.cancelNotify(MACH_NOTIFY_NO_SENDERS);
}
//
// Initiate service.
// This call will take control of the current thread and use it to service
-// incoming requests. The thread will not be released until an error happens.
+// incoming requests. The thread will not be released until an error happens, which
+// will cause an exception to be thrown. In other words, this never returns normally.
// We may also be creating additional threads to service concurrent requests
// as appropriate.
-// @@@ Additional threads are not being reaped at this point.
// @@@ Msg-errors in additional threads are not acted upon.
//
void MachServer::run(size_t maxSize, mach_msg_options_t options)
//
// This is the core of a server thread at work. It takes over the thread until
-// something makes it exit normally. Then it returns. Errors cause exceptions.
+// (a) an error occurs, throwing an exception
+// (b) low-load timeout happens, causing a normal return (doTimeout only)
// This code is loosely based on mach_msg_server.c, but is drifting away for
// various reasons of flexibility and resilience.
//
Message bufRequest(mMaxSize);
Message bufReply(mMaxSize);
- // all exits from runServerThread are through exceptions or "goto exit"
+ // all exits from runServerThread are through exceptions
try {
// register as a worker thread
- debug("machsrv", "%p starting service on port %d", this, int(mServerPort));
+ secdebug("machsrv", "%p starting service on port %d", this, int(mServerPort));
perThread().server = this;
for (;;) {
// perform self-timeout processing
if (doTimeout) {
if (workerCount > maxWorkerCount) {
- debug("machsrv", "%p too many threads; reaping immediately", this);
+ secdebug("machsrv", "%p too many threads; reaping immediately", this);
break;
}
Time::Absolute rightNow = Time::now();
if (rightNow >= nextCheckTime) { // reaping period complete; process
uint32 idlers = leastIdleWorkers;
- debug("machsrv", "%p end of reaping period: %ld (min) idle of %ld total",
+ secdebug("machsrv", "%p end of reaping period: %ld (min) idle of %ld total",
this, idlers, workerCount);
nextCheckTime = rightNow + workerTimeout;
leastIdleWorkers = INT_MAX;
// receive next IPC request (or wait for timeout)
switch (mach_msg_return_t mr = indefinite ?
- mach_msg_overwrite_trap(bufRequest,
+ mach_msg_overwrite(bufRequest,
MACH_RCV_MSG | mMsgOptions,
0, mMaxSize, mPortSet,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
(mach_msg_header_t *) 0, 0)
:
- mach_msg_overwrite_trap(bufRequest,
+ mach_msg_overwrite(bufRequest,
MACH_RCV_MSG | MACH_RCV_TIMEOUT | mMsgOptions,
0, mMaxSize, mPortSet,
mach_msg_timeout_t(timeout.mSeconds()), MACH_PORT_NULL,
} else {
// normal request message
{ StLock<Mutex> _(managerLock); idleCount--; }
- debug("machsrvreq",
+ secdebug("machsrvreq",
"servicing port %d request id=%d",
bufRequest.localPort().port(), bufRequest.msgId());
- if (bufRequest.localPort() == mServerPort) { // primary
+
+ // try subsidiary handlers first
+ bool handled = false;
+ for (HandlerSet::const_iterator it = mHandlers.begin();
+ it != mHandlers.end(); it++)
+ if (bufRequest.localPort() == (*it)->port()) {
+ (*it)->handle(bufRequest, bufReply);
+ handled = true;
+ }
+ if (!handled) {
+ // unclaimed, send to main handler
handle(bufRequest, bufReply);
- } else {
- for (HandlerSet::const_iterator it = mHandlers.begin();
- it != mHandlers.end(); it++)
- if (bufRequest.localPort() == (*it)->port())
- (*it)->handle(bufRequest, bufReply);
}
- debug("machsrvreq", "request complete");
+
+ secdebug("machsrvreq", "request complete");
{ StLock<Mutex> _(managerLock); idleCount++; }
}
* To avoid falling off the kernel's fast RPC path unnecessarily,
* we only supply MACH_SEND_TIMEOUT when absolutely necessary.
*/
- switch (mach_msg_return_t mr = mach_msg_overwrite_trap(bufReply,
+ switch (mach_msg_return_t mr = mach_msg_overwrite(bufReply,
(MACH_MSGH_BITS_REMOTE(bufReply.bits()) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG | mMsgOptions :
}
}
perThread().server = NULL;
- debug("machsrv", "%p ending service on port %d", this, int(mServerPort));
+ secdebug("machsrv", "%p ending service on port %d", this, int(mServerPort));
} catch (...) {
perThread().server = NULL;
- debug("machsrv", "%p aborted by exception (port %d)", this, int(mServerPort));
+ secdebug("machsrv", "%p aborted by exception (port %d)", this, int(mServerPort));
throw;
}
}
if (memory) {
set<Allocation> &releaseSet = perThread().deferredAllocations;
assert(releaseSet.find(Allocation(memory, alloc)) == releaseSet.end());
- debug("machsrvmem", "%p register %p for release with %p",
+ secdebug("machsrvmem", "%p register %p for release with %p",
this, memory, &alloc);
releaseSet.insert(Allocation(memory, alloc));
}
{
set<Allocation> &releaseSet = perThread().deferredAllocations;
for (set<Allocation>::iterator it = releaseSet.begin(); it != releaseSet.end(); it++) {
- debug("machsrvmem", "%p release %p with %p", this, it->addr, it->allocator);
+ secdebug("machsrvmem", "%p release %p with %p", this, it->addr, it->allocator);
it->allocator->free(it->addr);
}
releaseSet.erase(releaseSet.begin(), releaseSet.end());
StLock<Mutex> _(managerLock);
workerCount++;
idleCount++;
- debug("machsrv", "%p adding worker thread (%ld workers, %ld idle)",
+ secdebug("machsrv", "%p adding worker thread (%ld workers, %ld idle)",
this, workerCount, idleCount);
workers.insert(thread);
}
StLock<Mutex> _(managerLock);
workerCount--;
idleCount--;
- debug("machsrv", "%p removing worker thread (%ld workers, %ld idle)",
+ secdebug("machsrv", "%p removing worker thread (%ld workers, %ld idle)",
this, workerCount, idleCount);
workers.erase(thread);
}
if (!(top = static_cast<Timer *>(timers.pop(Time::now()))))
return false; // nothing (more) to be done now
} // drop lock; work has been retrieved
- debug("machsrvtime", "%p timer %p executing at %.3f",
+ secdebug("machsrvtime", "%p timer %p executing at %.3f",
this, top, Time::now().internalForm());
try {
top->action();
- debug("machsrvtime", "%p timer %p done", this, top);
+ secdebug("machsrvtime", "%p timer %p done", this, top);
} catch (...) {
- debug("machsrvtime", "%p server timer %p failed with exception", this, top);
+ secdebug("machsrvtime", "%p server timer %p failed with exception", this, top);
}
return true;
}
void MachServer::notifyPortDestroyed(Port) { }
-void cdsa_mach_notify_send_once(mach_port_t)
-{ MachServer::active().notifySendOnce(); }
+void cdsa_mach_notify_send_once(mach_port_t port)
+{ MachServer::active().notifySendOnce(port); }
+
+void MachServer::notifySendOnce(Port) { }
-void MachServer::notifySendOnce() { }
+void cdsa_mach_notify_no_senders(mach_port_t port, mach_port_mscount_t count)
+{ MachServer::active().notifyNoSenders(port, count); }
-void cdsa_mach_notify_no_senders(mach_port_t)
-{ /* legacy handler - not used by system */ }
+void MachServer::notifyNoSenders(Port, mach_port_mscount_t) { }
} // end namespace MachPlusPlus