osx - launchd: sleep in GCD managed signal handler -


i encounter strange situation in launchd managed daemon when try sleep in sigterm handler managed grand central dispatch described here.
works fine , sigterm signal handler before receiving sigkill when not sleep in sigterm handler. sleep -- extremly short amounts of time usleep(1); -- not sigterm handler @ instead daemon sigkilled instantly launchd.

btw using enabletransactions in plist file , proper vproc_transaction_begin(3)/vproc_transaction_end(3) code described here.

not sleeping in sigterm handler not option me because need poll information "client processess" know if save end daemon or not.

it seems me if there compiler flag responsible directly receiving sigkill (and not expected sigterm) sleep in signal handler because when sleep not see outputs of sigterm handler @ all. expect see debug prints sleep call not case.

here plist file:

  <?xml version="1.0" encoding="utf-8"?>     <!doctype plist public "-//apple//dtd plist 1.0//en" "http://www.apple.com/dtds/propertylist-1.0.dtd">     <plist version="1.0">     <dict>             <key>label</key>             <string>org.example.mytestd</string>             <key>programarguments</key>             <array>                     <string>/usr/sbin/mytestd</string>             </array>              <key>runatload</key>             <true/>              <key>keepalive</key>             <true/>              <key>exittimeout</key>             <integer>0</integer>              <key>enabletransactions</key>             <true/>     </dict>     </plist> 

and here sigterm handler. please note see output @ add usleep(1); line.

static void mysignalhandler(int sigraised) {     int fd = open("/tmp/mytestd.log", o_wronly | o_creat | o_append, 0777);     if (fd <= 0) return;      dprintf(fd, "%s(): signal received = %d, sleeping time ...\n", __func__, sigraised);     usleep(1);     dprintf(fd, "%s(): ... done\n", __func__);      dprintf(fd, "%s(): exiting\n", __func__);     close(fd);      // transactionhandle global variable assigned in daemon main     if (transactionhandle) vproc_transaction_end(null, transactionhandle);      exit(0); } 

thank hints/answers!

chris

i think crux of issue have in plist:

        <key>exittimeout</key>         <integer>0</integer> 

the man page launchd.plist says:

exittimeout <integer>

the amount of time launchd waits between sending sigterm signal , before sending sigkill signal when job stopped. default value system-defined. value 0 interpreted infinity , should not used, can stall system shutdown forever.

experimenting bit, appears text not accurate. empirically, observe if value set 0, behavior you're describing (where process killed after receiving term, regardless of outstanding declared transactions.) if change value arbitrary larger number like, say, 60, observe term handler being called , having chance cleanup before exiting.

it's not entirely clear whether you're using classic signal handling or gcd since link posted describes both, if you're using classic unix signal handling, should mention you've called functions in signal handler aren't on list of functions ok call in signal handlers (dprintf , usleep aren't on list.) seems more you're using gcd.

another thing occurs me if using vproc_transaction_begin/end bracket whatever work items you're waiting in handler, behavior "for free" without needing signal handler @ all. it's conceivable there centralized cleanup work need irrespective of normal work items, if waiting other asynchronous tasks finish, simpler.

anyway, in case helps, here's code used test scenario:

#import <foundation/foundation.h>  #import <vproc.h>  static void signalhandler(int sigraised); static void fakework(); static void log(nsstring* str);  int64_t outstandingtransactions; dispatch_source_t fakeworkgeneratortimer;  int main(int argc, const char * argv[]) {     @autoreleasepool     {         // set gcd handler sigterm         dispatch_source_t source = dispatch_source_create(dispatch_source_type_signal, sigterm, 0, dispatch_get_global_queue(0, 0));         dispatch_source_set_event_handler(source, ^{             signalhandler(sigterm);         });         dispatch_resume(source);          // tell standard signal handling mechanism ignore sigterm         struct sigaction action = { 0 };         action.sa_handler = sig_ign;         sigaction(sigterm, &action, null);          // set 10hz timer generate "fake work" events         fakeworkgeneratortimer = dispatch_source_create(dispatch_source_type_timer, 0, 0, dispatch_get_global_queue(0, 0));         dispatch_source_set_timer(fakeworkgeneratortimer, dispatch_time_now, 0.1 * nsec_per_sec, 0.05 * nsec_per_sec);         dispatch_source_set_event_handler(fakeworkgeneratortimer, ^{             // dont add event *every* time...             if (arc4random_uniform(10) >= 5) dispatch_async(dispatch_get_global_queue(0, 0), ^{ fakework(); });         });         dispatch_resume(fakeworkgeneratortimer);          // start run loop         while (1)         {             // runloop listens sigterm , return here, i'm sending right in.             [[nsrunloop currentrunloop] run];         }     }      return 0; }  static void signalhandler(int sigraised) {     // open transaction dont killed before getting end of handler     vproc_transaction_t transaction = vproc_transaction_begin(null);      // turn off fake work generator     dispatch_suspend(fakeworkgeneratortimer);      log([nsstring stringwithformat: @"%s(): signal received = %d\n", __func__, sigraised]);      int64_t transcount = outstandingtransactions;     while (transcount > 0)     {         log([nsstring stringwithformat: @"%s(): %lld transactions outstanding. waiting...\n", __func__, transcount]);         usleep(usec_per_sec / 4);         transcount = outstandingtransactions;     }      log([nsstring stringwithformat: @"%s(): exiting\n", __func__]);      // close transaction     vproc_transaction_end(null, transaction);      exit(0); }  static void fakework() {     static int64_t workunitnumber;      const nstimeinterval minworkduration = 1.0 / 100.0; // 10ms     const nstimeinterval maxworkduration = 4.0; // 4s      osatomicincrement64barrier(&outstandingtransactions);     int64_t serialnum = osatomicincrement64barrier(&workunitnumber);     vproc_transaction_t transaction = vproc_transaction_begin(null);      log([nsstring stringwithformat: @"starting work unit: %@", @(serialnum)]);      // set callback random time later.     int64_t taskduration = arc4random_uniform(nsec_per_sec * (maxworkduration - minworkduration)) + (minworkduration * nsec_per_sec);     dispatch_after(dispatch_time(dispatch_time_now, taskduration), dispatch_get_global_queue(0, 0), ^{         log([nsstring stringwithformat: @"finishing work unit: %@", @(serialnum)]);         vproc_transaction_end(null, transaction);         osatomicdecrement64barrier(&outstandingtransactions);     }); }  static void log(nsstring* str) {     static nsobject* lockobj = nil;     static dispatch_once_t oncetoken;     dispatch_once(&oncetoken, ^{         lockobj = [nsobject new];     });      @synchronized(lockobj)     {         int fd = open("/tmp/mytestd.log", o_wronly | o_creat | o_append, 0777);         if (fd <= 0) return;         dprintf(fd, "%s\n", str.utf8string);         close(fd);     } } 

and plist:

<?xml version="1.0" encoding="utf-8"?> <!doctype plist public "-//apple//dtd plist 1.0//en" "http://www.apple.com/dtds/propertylist-1.0.dtd"> <plist version="1.0">     <dict>         <key>label</key>         <string>daemondeathtest</string>         <key>programarguments</key>         <array>             <string>/tmp/bin/daemondeathtest</string>         </array>          <key>runatload</key>         <true/>          <key>keepalive</key>         <true/>          <key>exittimeout</key>         <integer>60</integer>          <key>enabletransactions</key>         <true/>     </dict> </plist> 

Comments

Popular posts from this blog

php - Submit Form Data without Reloading page -

linux - Rails running on virtual machine in Windows -

php - $params->set Array between square bracket -