trunk/talk git-svn-id: http://webrtc.googlecode.com/svn/trunk@4318 4adac7df-926f-26a2-2b94-8c16560cd09d
		
			
				
	
	
		
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
 | 
						|
#include "talk/base/macsocketserver.h"
 | 
						|
 | 
						|
#include "talk/base/common.h"
 | 
						|
#include "talk/base/logging.h"
 | 
						|
#include "talk/base/macasyncsocket.h"
 | 
						|
#include "talk/base/macutils.h"
 | 
						|
#include "talk/base/thread.h"
 | 
						|
 | 
						|
namespace talk_base {
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// MacBaseSocketServer
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
MacBaseSocketServer::MacBaseSocketServer() {
 | 
						|
}
 | 
						|
 | 
						|
MacBaseSocketServer::~MacBaseSocketServer() {
 | 
						|
}
 | 
						|
 | 
						|
AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int type) {
 | 
						|
  return CreateAsyncSocket(AF_INET, type);
 | 
						|
}
 | 
						|
 | 
						|
AsyncSocket* MacBaseSocketServer::CreateAsyncSocket(int family, int type) {
 | 
						|
  if (SOCK_STREAM != type)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  MacAsyncSocket* socket = new MacAsyncSocket(this, family);
 | 
						|
  if (!socket->valid()) {
 | 
						|
    delete socket;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  return socket;
 | 
						|
}
 | 
						|
 | 
						|
void MacBaseSocketServer::RegisterSocket(MacAsyncSocket* s) {
 | 
						|
  sockets_.insert(s);
 | 
						|
}
 | 
						|
 | 
						|
void MacBaseSocketServer::UnregisterSocket(MacAsyncSocket* s) {
 | 
						|
  VERIFY(1 == sockets_.erase(s));   // found 1
 | 
						|
}
 | 
						|
 | 
						|
bool MacBaseSocketServer::SetPosixSignalHandler(int signum,
 | 
						|
                                                void (*handler)(int)) {
 | 
						|
  Dispatcher* dispatcher = signal_dispatcher();
 | 
						|
  if (!PhysicalSocketServer::SetPosixSignalHandler(signum, handler)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Only register the FD once, when the first custom handler is installed.
 | 
						|
  if (!dispatcher && (dispatcher = signal_dispatcher())) {
 | 
						|
    CFFileDescriptorContext ctx = { 0 };
 | 
						|
    ctx.info = this;
 | 
						|
 | 
						|
    CFFileDescriptorRef desc = CFFileDescriptorCreate(
 | 
						|
        kCFAllocatorDefault,
 | 
						|
        dispatcher->GetDescriptor(),
 | 
						|
        false,
 | 
						|
        &MacBaseSocketServer::FileDescriptorCallback,
 | 
						|
        &ctx);
 | 
						|
    if (!desc) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    CFFileDescriptorEnableCallBacks(desc, kCFFileDescriptorReadCallBack);
 | 
						|
    CFRunLoopSourceRef ref =
 | 
						|
        CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, desc, 0);
 | 
						|
 | 
						|
    if (!ref) {
 | 
						|
      CFRelease(desc);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    CFRunLoopAddSource(CFRunLoopGetCurrent(), ref, kCFRunLoopCommonModes);
 | 
						|
    CFRelease(desc);
 | 
						|
    CFRelease(ref);
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
// Used to disable socket events from waking our message queue when
 | 
						|
// process_io is false.  Does not disable signal event handling though.
 | 
						|
void MacBaseSocketServer::EnableSocketCallbacks(bool enable) {
 | 
						|
  for (std::set<MacAsyncSocket*>::iterator it = sockets().begin();
 | 
						|
       it != sockets().end(); ++it) {
 | 
						|
    if (enable) {
 | 
						|
      (*it)->EnableCallbacks();
 | 
						|
    } else {
 | 
						|
      (*it)->DisableCallbacks();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MacBaseSocketServer::FileDescriptorCallback(CFFileDescriptorRef fd,
 | 
						|
                                                 CFOptionFlags flags,
 | 
						|
                                                 void* context) {
 | 
						|
  MacBaseSocketServer* this_ss =
 | 
						|
      reinterpret_cast<MacBaseSocketServer*>(context);
 | 
						|
  ASSERT(this_ss);
 | 
						|
  Dispatcher* signal_dispatcher = this_ss->signal_dispatcher();
 | 
						|
  ASSERT(signal_dispatcher);
 | 
						|
 | 
						|
  signal_dispatcher->OnPreEvent(DE_READ);
 | 
						|
  signal_dispatcher->OnEvent(DE_READ, 0);
 | 
						|
  CFFileDescriptorEnableCallBacks(fd, kCFFileDescriptorReadCallBack);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// MacCFSocketServer
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void WakeUpCallback(void* info) {
 | 
						|
  MacCFSocketServer* server = static_cast<MacCFSocketServer*>(info);
 | 
						|
  ASSERT(NULL != server);
 | 
						|
  server->OnWakeUpCallback();
 | 
						|
}
 | 
						|
 | 
						|
MacCFSocketServer::MacCFSocketServer()
 | 
						|
    : run_loop_(CFRunLoopGetCurrent()),
 | 
						|
      wake_up_(NULL) {
 | 
						|
  CFRunLoopSourceContext ctx;
 | 
						|
  memset(&ctx, 0, sizeof(ctx));
 | 
						|
  ctx.info = this;
 | 
						|
  ctx.perform = &WakeUpCallback;
 | 
						|
  wake_up_ = CFRunLoopSourceCreate(NULL, 0, &ctx);
 | 
						|
  ASSERT(NULL != wake_up_);
 | 
						|
  if (wake_up_) {
 | 
						|
    CFRunLoopAddSource(run_loop_, wake_up_, kCFRunLoopCommonModes);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MacCFSocketServer::~MacCFSocketServer() {
 | 
						|
  if (wake_up_) {
 | 
						|
    CFRunLoopSourceInvalidate(wake_up_);
 | 
						|
    CFRelease(wake_up_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool MacCFSocketServer::Wait(int cms, bool process_io) {
 | 
						|
  ASSERT(CFRunLoopGetCurrent() == run_loop_);
 | 
						|
 | 
						|
  if (!process_io && cms == 0) {
 | 
						|
    // No op.
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!process_io) {
 | 
						|
    // No way to listen to common modes and not get socket events, unless
 | 
						|
    // we disable each one's callbacks.
 | 
						|
    EnableSocketCallbacks(false);
 | 
						|
  }
 | 
						|
 | 
						|
  SInt32 result;
 | 
						|
  if (kForever == cms) {
 | 
						|
    do {
 | 
						|
      // Would prefer to run in a custom mode that only listens to wake_up,
 | 
						|
      // but we have qtkit sending work to the main thread which is effectively
 | 
						|
      // blocked here, causing deadlock.  Thus listen to the common modes.
 | 
						|
      // TODO: If QTKit becomes thread safe, do the above.
 | 
						|
      result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000000, false);
 | 
						|
    } while (result != kCFRunLoopRunFinished && result != kCFRunLoopRunStopped);
 | 
						|
  } else {
 | 
						|
    // TODO: In the case of 0ms wait, this will only process one event, so we
 | 
						|
    // may want to loop until it returns TimedOut.
 | 
						|
    CFTimeInterval seconds = cms / 1000.0;
 | 
						|
    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, false);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!process_io) {
 | 
						|
    // Reenable them.  Hopefully this won't cause spurious callbacks or
 | 
						|
    // missing ones while they were disabled.
 | 
						|
    EnableSocketCallbacks(true);
 | 
						|
  }
 | 
						|
 | 
						|
  if (kCFRunLoopRunFinished == result) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void MacCFSocketServer::WakeUp() {
 | 
						|
  if (wake_up_) {
 | 
						|
    CFRunLoopSourceSignal(wake_up_);
 | 
						|
    CFRunLoopWakeUp(run_loop_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MacCFSocketServer::OnWakeUpCallback() {
 | 
						|
  ASSERT(run_loop_ == CFRunLoopGetCurrent());
 | 
						|
  CFRunLoopStop(run_loop_);
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// MacCarbonSocketServer
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
#ifndef CARBON_DEPRECATED
 | 
						|
 | 
						|
const UInt32 kEventClassSocketServer = 'MCSS';
 | 
						|
const UInt32 kEventWakeUp = 'WAKE';
 | 
						|
const EventTypeSpec kEventWakeUpSpec[] = {
 | 
						|
  { kEventClassSocketServer, kEventWakeUp }
 | 
						|
};
 | 
						|
 | 
						|
std::string DecodeEvent(EventRef event) {
 | 
						|
  std::string str;
 | 
						|
  DecodeFourChar(::GetEventClass(event), &str);
 | 
						|
  str.push_back(':');
 | 
						|
  DecodeFourChar(::GetEventKind(event), &str);
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
MacCarbonSocketServer::MacCarbonSocketServer()
 | 
						|
    : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) {
 | 
						|
  VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
 | 
						|
                              kEventAttributeUserEvent, &wake_up_));
 | 
						|
}
 | 
						|
 | 
						|
MacCarbonSocketServer::~MacCarbonSocketServer() {
 | 
						|
  if (wake_up_) {
 | 
						|
    ReleaseEvent(wake_up_);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool MacCarbonSocketServer::Wait(int cms, bool process_io) {
 | 
						|
  ASSERT(GetCurrentEventQueue() == event_queue_);
 | 
						|
 | 
						|
  // Listen to all events if we're processing I/O.
 | 
						|
  // Only listen for our wakeup event if we're not.
 | 
						|
  UInt32 num_types = 0;
 | 
						|
  const EventTypeSpec* events = NULL;
 | 
						|
  if (!process_io) {
 | 
						|
    num_types = GetEventTypeCount(kEventWakeUpSpec);
 | 
						|
    events = kEventWakeUpSpec;
 | 
						|
  }
 | 
						|
 | 
						|
  EventTargetRef target = GetEventDispatcherTarget();
 | 
						|
  EventTimeout timeout =
 | 
						|
      (kForever == cms) ? kEventDurationForever : cms / 1000.0;
 | 
						|
  EventTimeout end_time = GetCurrentEventTime() + timeout;
 | 
						|
 | 
						|
  bool done = false;
 | 
						|
  while (!done) {
 | 
						|
    EventRef event;
 | 
						|
    OSStatus result = ReceiveNextEvent(num_types, events, timeout, true,
 | 
						|
                                       &event);
 | 
						|
    if (noErr == result) {
 | 
						|
      if (wake_up_ != event) {
 | 
						|
        LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event);
 | 
						|
        result = SendEventToEventTarget(event, target);
 | 
						|
        if ((noErr != result) && (eventNotHandledErr != result)) {
 | 
						|
          LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget";
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        done = true;
 | 
						|
      }
 | 
						|
      ReleaseEvent(event);
 | 
						|
    } else if (eventLoopTimedOutErr == result) {
 | 
						|
      ASSERT(cms != kForever);
 | 
						|
      done = true;
 | 
						|
    } else if (eventLoopQuitErr == result) {
 | 
						|
      // Ignore this... we get spurious quits for a variety of reasons.
 | 
						|
      LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent";
 | 
						|
    } else {
 | 
						|
      // Some strange error occurred. Log it.
 | 
						|
      LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent";
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    if (kForever != cms) {
 | 
						|
      timeout = end_time - GetCurrentEventTime();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void MacCarbonSocketServer::WakeUp() {
 | 
						|
  if (!IsEventInQueue(event_queue_, wake_up_)) {
 | 
						|
    RetainEvent(wake_up_);
 | 
						|
    OSStatus result = PostEventToQueue(event_queue_, wake_up_,
 | 
						|
                                       kEventPriorityStandard);
 | 
						|
    if (noErr != result) {
 | 
						|
      LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// MacCarbonAppSocketServer
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
MacCarbonAppSocketServer::MacCarbonAppSocketServer()
 | 
						|
    : event_queue_(GetCurrentEventQueue()) {
 | 
						|
  // Install event handler
 | 
						|
  VERIFY(noErr == InstallApplicationEventHandler(
 | 
						|
      NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this,
 | 
						|
      &event_handler_));
 | 
						|
 | 
						|
  // Install a timer and set it idle to begin with.
 | 
						|
  VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(),
 | 
						|
                                        kEventDurationForever,
 | 
						|
                                        kEventDurationForever,
 | 
						|
                                        NewEventLoopTimerUPP(TimerHandler),
 | 
						|
                                        this,
 | 
						|
                                        &timer_));
 | 
						|
}
 | 
						|
 | 
						|
MacCarbonAppSocketServer::~MacCarbonAppSocketServer() {
 | 
						|
  RemoveEventLoopTimer(timer_);
 | 
						|
  RemoveEventHandler(event_handler_);
 | 
						|
}
 | 
						|
 | 
						|
OSStatus MacCarbonAppSocketServer::WakeUpEventHandler(
 | 
						|
    EventHandlerCallRef next, EventRef event, void *data) {
 | 
						|
  QuitApplicationEventLoop();
 | 
						|
  return noErr;
 | 
						|
}
 | 
						|
 | 
						|
void MacCarbonAppSocketServer::TimerHandler(
 | 
						|
    EventLoopTimerRef timer, void *data) {
 | 
						|
  QuitApplicationEventLoop();
 | 
						|
}
 | 
						|
 | 
						|
bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) {
 | 
						|
  if (!process_io && cms == 0) {
 | 
						|
    // No op.
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  if (kForever != cms) {
 | 
						|
    // Start a timer.
 | 
						|
    OSStatus error =
 | 
						|
        SetEventLoopTimerNextFireTime(timer_, cms / 1000.0);
 | 
						|
    if (error != noErr) {
 | 
						|
      LOG(LS_ERROR) << "Failed setting next fire time.";
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (!process_io) {
 | 
						|
    // No way to listen to common modes and not get socket events, unless
 | 
						|
    // we disable each one's callbacks.
 | 
						|
    EnableSocketCallbacks(false);
 | 
						|
  }
 | 
						|
  RunApplicationEventLoop();
 | 
						|
  if (!process_io) {
 | 
						|
    // Reenable them.  Hopefully this won't cause spurious callbacks or
 | 
						|
    // missing ones while they were disabled.
 | 
						|
    EnableSocketCallbacks(true);
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void MacCarbonAppSocketServer::WakeUp() {
 | 
						|
  // TODO: No-op if there's already a WakeUp in flight.
 | 
						|
  EventRef wake_up;
 | 
						|
  VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0,
 | 
						|
                              kEventAttributeUserEvent, &wake_up));
 | 
						|
  OSStatus result = PostEventToQueue(event_queue_, wake_up,
 | 
						|
                                       kEventPriorityStandard);
 | 
						|
  if (noErr != result) {
 | 
						|
    LOG_E(LS_ERROR, OS, result) << "PostEventToQueue";
 | 
						|
  }
 | 
						|
  ReleaseEvent(wake_up);
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
} // namespace talk_base
 |