// // GTMSignalHandler.m // // Copyright 2008 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. // #import "GTMSignalHandler.h" #import "GTMDefines.h" #import #import "GTMDebugSelectorValidation.h" #pragma clang diagnostic push // Ignore all of the deprecation warnings for GTMSignalHandler #pragma clang diagnostic ignored "-Wdeprecated-implementations" // Simplifying assumption: No more than one handler for a particular signal is // alive at a time. When the second signal is registered, kqueue just updates // the info about the first signal, which makes -dealloc time complicated (what // happens when handler1(SIGUSR1) is released before handler2(SIGUSR1)?). This // could be solved by having one kqueue per signal, or keeping a list of // handlers interested in a particular signal, but not really worth it for apps // that register the handlers at startup and don't change them. @implementation GTMSignalHandler -(id)init { // Folks shouldn't call init directly, so they get what they deserve. _GTMDevLog(@"Don't call init, use " @"initWithSignal:target:action:"); return [self initWithSignal:0 target:nil action:NULL]; } - (id)initWithSignal:(int)signo target:(id)target action:(SEL)action { if ((self = [super init])) { if (signo == 0) { [self release]; return nil; } GTMAssertSelectorNilOrImplementedWithArguments(target, action, @encode(int), NULL); // We're handling this signal via libdispatch, so turn off the usual signal // handling. if (signal(signo, SIG_IGN) == SIG_ERR) { _GTMDevLog(@"could not ignore signal %d. Errno %d", signo, errno); // COV_NF_LINE } if (action != NULL) { signalSource_ = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, signo, 0, dispatch_get_main_queue()); dispatch_source_set_event_handler(signalSource_, ^(void) { NSMethodSignature *methodSig = [target methodSignatureForSelector:action]; _GTMDevAssert(methodSig != nil, @"failed to get the signature?"); NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; [invocation setTarget:target]; [invocation setSelector:action]; [invocation setArgument:(void*)&signo atIndex:2]; [invocation invoke]; }); dispatch_resume(signalSource_); } } return self; } - (void)dealloc { [self invalidate]; [super dealloc]; } - (void)invalidate { if (signalSource_) { dispatch_source_cancel(signalSource_); dispatch_release(signalSource_); signalSource_ = NULL; } } @end #pragma clang diagnostic pop