IO-SocketAlarm

 view release on metacpan or  search on metacpan

SocketAlarm.xs  view on Meta::CPAN

   MAGIC *magic;
   if (sa->owner)
      croak("BUG: already attached to perl object");
   sa->owner= obj_inner_sv;
   magic= sv_magicext((SV*) sa->owner, NULL, PERL_MAGIC_ext, &socketalarm_magic_vt, (const char*) sa, 0);
#ifdef USE_ITHREADS
   magic->mg_flags |= MGf_DUP;
#else
   (void)magic; // suppress 'unused' warning
#endif
}

// Return existing Watch object, or create a new one.
// Returned SV has a non-mortal refcount, which is what the typemap
// wants for returning a "struct socketalarm*" to perl-land
static SV* wrap_socketalarm(struct socketalarm *sa) {
   SV *obj;
   HV *hv;
   // Since this is used in typemap, handle NULL gracefully
   if (!sa)
      return &PL_sv_undef;
   // If there is already a node object, return a new reference to it.
   if (sa->owner)
      return newRV_inc((SV*) sa->owner);
   // else create a node object
   hv= newHV();
   obj= newRV_noinc((SV*) hv);
   sv_bless(obj, gv_stashpv("IO::SocketAlarm", GV_ADD));
   attach_magic_socketalarm((SV*) hv, sa);
   return obj;
}

#define EXPORT_ENUM(x) newCONSTSUB(stash, #x, new_enum_dualvar(aTHX_ x, newSVpvs_share(#x)))
static SV * new_enum_dualvar(pTHX_ IV ival, SV *name) {
   SvUPGRADE(name, SVt_PVNV);
   SvIV_set(name, ival);
   SvIOK_on(name);
   SvREADONLY_on(name);
   return name;
}

/*------------------------------------------------------------------------------------
 * Perl API
 */

MODULE = IO::SocketAlarm               PACKAGE = IO::SocketAlarm

void
_init_socketalarm(self, sock_sv, eventmask_sv, actions_sv)
   SV *self
   SV *sock_sv
   SV *eventmask_sv
   SV *actions_sv
   INIT:
      int sock_fd= fileno_from_sv(sock_sv);
      int eventmask= EVENT_DEFAULTS;
      struct stat statbuf;
      struct socketalarm *sa;
      SV **action_list= NULL;
      SSize_t n_actions= 0;
   PPCODE:
      if (!sv_isobject(self))
         croak("Not an object");
      if ((sa= get_magic_socketalarm(self, 0)))
         croak("Already initialized");
      if (!(sock_fd >= 0 && fstat(sock_fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)))
         croak("Not an open socket");
      if (eventmask_sv && SvOK(eventmask_sv))
         eventmask= SvIV(eventmask_sv);
      if (actions_sv && SvOK(actions_sv)) {
         action_list= unwrap_array(actions_sv, &n_actions);
         if (!action_list)
            croak("Actions must be an arrayref (or undefined)");
      }
      sa= socketalarm_new(sock_fd, &statbuf, eventmask, action_list, n_actions);
      attach_magic_socketalarm(SvRV(self), sa);
      XSRETURN(1); // return $self

int
socket(alarm)
   struct socketalarm *alarm
   CODE:
      RETVAL= alarm->watch_fd;
   OUTPUT:
      RETVAL

int
events(alarm)
   struct socketalarm *alarm
   CODE:
      RETVAL= alarm->event_mask;
   OUTPUT:
      RETVAL

void
actions(alarm)
   struct socketalarm *alarm
   PPCODE:
      if (!alarm->actions_av);
         socketalarm__build_actions(alarm);
      ST(0)= sv_2mortal(newRV_inc((SV*) alarm->actions_av));
      XSRETURN(1);

int
action_count(alarm)
   struct socketalarm *alarm
   CODE:
      RETVAL= alarm->action_count;
   OUTPUT:
      RETVAL

int
cur_action(alarm)
   struct socketalarm *alarm
   CODE:
      watch_list_item_get_status(alarm, &RETVAL);
   OUTPUT:
      RETVAL

bool
start(alarm)
   struct socketalarm *alarm
   CODE:
      RETVAL= watch_list_add(alarm);
   OUTPUT:
      RETVAL

bool
cancel(alarm)
   struct socketalarm *alarm
   CODE:
      RETVAL= watch_list_remove(alarm);
   OUTPUT:
      RETVAL

SV*
stringify(alarm)
   struct socketalarm *alarm
   INIT:
      SV *out= sv_2mortal(newSVpvn("",0));
      Size_t i;
   CODE:
      sv_catpvf(out, "watch fd: %d\n", alarm->watch_fd);
      sv_catpvf(out, "event mask:%s%s\n",
         alarm->event_mask & EVENT_SHUT? " SHUT":"",
         alarm->event_mask & EVENT_CLOSE? " CLOSE":""
      );
      sv_catpv(out, "actions:\n");
      for (i= 0; i < alarm->action_count; i++) {
         char buf[256];
         snprint_action(buf, sizeof(buf), alarm->actions+i);
         sv_catpvf(out, "%4d: %s\n", (int)i, buf);
      }
      SvREFCNT_inc(out);
      RETVAL= out;
   OUTPUT:
      RETVAL

void
_terminate_all()
   PPCODE:
      shutdown_watch_thread();

MODULE = IO::SocketAlarm               PACKAGE = IO::SocketAlarm::Util

struct socketalarm *
socketalarm(sock_sv, ...)
   SV *sock_sv
   INIT:
      int sock_fd= fileno_from_sv(sock_sv);
      int eventmask= EVENT_DEFAULTS;
      int action_ofs= 1;
      struct stat statbuf;
   CODE:
      if (!(sock_fd >= 0 && fstat(sock_fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)))
         croak("Not an open socket");
      if (items > 1) {
         // must either be a scalar, a scalar followed by actions specs, or action specs
         if (SvOK(ST(1)) && looks_like_number(ST(1))) {
            eventmask= SvIV(ST(1));
            action_ofs++;
         }
      }
      RETVAL= socketalarm_new(sock_fd, &statbuf, eventmask, &(ST(action_ofs)), items - action_ofs);
      watch_list_add(RETVAL);
   OUTPUT:
      RETVAL

bool
is_socket(fd_sv)
   SV *fd_sv
   INIT:
      int fd= fileno_from_sv(fd_sv);
      struct stat statbuf;
   CODE:
      RETVAL= fd >= 0 && fstat(fd, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode);
   OUTPUT:
      RETVAL

SV *
get_fd_table_str(max_fd=1024)
   int max_fd
   INIT:
      SV *out= newSVpvn("",0);
      size_t avail= 0, needed= 1023;
   CODE:
      // FD status could change between calls, changing the length requirement, so loop.
      // 'avail' count includes the NUL byte, and 'needed' does not.
      while (avail <= needed) {
         sv_grow(out, needed+1);
         avail= needed+1;
         needed= snprint_fd_table(SvPVX(out), avail, max_fd);
      }
      SvCUR_set(out, needed);
      RETVAL= out;
   OUTPUT:
      RETVAL

# For unit test purposes only, export _poll that polls on a single file
# descriptor to verify the statuses for sockets in various states.

void
_poll(fd, events, timeout)
   int fd
   SV *events;
   int timeout;
   INIT:
      int ret;
      struct pollfd pollbuf;
   PPCODE:
      pollbuf.fd= fd;
      pollbuf.events= SvIV(events);
      pollbuf.revents= 0;
      ret= poll(&pollbuf, 1, timeout);
      EXTEND(SP, 2);
      PUSHs(sv_2mortal(newSViv(ret)));
      PUSHs(sv_2mortal(newSViv(ret < 0? errno : ret > 0? pollbuf.revents : 0)));

#-----------------------------------------------------------------------------
#  Constants
#

BOOT:
   HV* stash= gv_stashpvn("IO::SocketAlarm::Util", 21, GV_ADD);
   EXPORT_ENUM(EVENT_SHUT);
   EXPORT_ENUM(EVENT_EOF);
   EXPORT_ENUM(EVENT_IN);
   EXPORT_ENUM(EVENT_PRI);
   EXPORT_ENUM(EVENT_CLOSE);
   EXPORT_ENUM(POLLIN);
   EXPORT_ENUM(POLLOUT);
   EXPORT_ENUM(POLLPRI);
   EXPORT_ENUM(POLLERR);
   EXPORT_ENUM(POLLHUP);
#ifdef POLLRDHUP
   EXPORT_ENUM(POLLRDHUP);
#endif
   EXPORT_ENUM(POLLNVAL);

PROTOTYPES: DISABLE



( run in 1.649 second using v1.01-cache-2.11-cpan-71847e10f99 )