Forgive me Father, for I have used "goto"

djberg96 on 2004-10-29T04:49:54

Well, it took 7 years, but I finally found a use for "goto" outside of BASIC. In my particular case, it was within a Ruby C extension. I wanted to create 'tail -f' functionality for monitoring the Windows event log. It makes sense when you take blocks into consideration. To wit:

e = EventLog.open("Application")

e.tail{ |rec| puts "Record added to log" p rec }
The tail method sits in an infinite loop (which is accomplished with the 'goto'), yielding a (custom) struct every time a record is written to the event log.

On a stranger note, the Windows function NotifyChangeEventLog() can only detect a maximum of one change every five seconds. That means, even if 20 records were written to the event log in under 5 seconds, that block would only pick up the first one. No, there's no way around it.


I don't see the goto

btilly on 2004-10-29T20:31:45

You aren't showing the code where you felt it necessary to use a goto, so I cannot judge its validity. I suspect that it isn't needed though.

For the record, I know of exactly two necessary uses of goto in Perl.

Re:I don't see the goto

djberg96 on 2004-10-29T22:57:22

static VALUE eventlog_tail(VALUE self){
   ELS* ptr;
   VALUE rbBlock, rbSource, rbServer;
   HANDLE hEvent;
   DWORD dwWaitResult;
   int rv;

   Data_Get_Struct(self,ELS,ptr);

   eventblock:
   hEvent = CreateEvent(
      NULL,    // default security attributes
      FALSE,   // no manual reset
      FALSE,   // create as not signaled
      NULL     // no event name
   );

   rv = NotifyChangeEventLog(ptr->hEventLog, hEvent);
   if(0 == rv){
      rb_raise(cEventLogError,ErrorDescription(GetLastError()));
   }

   dwWaitResult = WaitForSingleObject(hEvent, INFINITE);

   if(dwWaitResult == WAIT_FAILED){
      CloseHandle(hEvent);
      rb_raise(cEventLogError,
         "WaitForSingleObject() failed in notify() method");
   }
   else{
      VALUE rbLast;
      rbSource = rb_iv_get(self,"@source");
      rbServer = rb_iv_get(self,"@server");

      rbLast = read_last_event(ptr->hEventLog,rbSource,rbServer);

      if(rb_block_given_p()){
         rbBlock = rb_block_proc();
      }
      else{
         rb_raise(cEventLogError,"block missing for notify_change()");
      }
      rb_funcall(rbBlock,rb_intern("call"),1,rbLast);
   }

   CloseHandle(hEvent);
   goto eventblock;
   return self;
}

Re:I don't see the goto

btilly on 2004-10-30T00:59:48

First of all that is written in C, not Ruby. There are a number of situations where you need a goto in C that you wouldn't in Perl or Ruby because of the availability of named loop control.

Secondly I don't see why you aren't using while to get the infinite loop rather than goto.

Thirdly you can save some lines by not always using braces. For instance:
      if(rb_block_given_p())
         rbBlock = rb_block_proc();
      else
         rb_raise(cEventLogError,"block missing for notify_change()");

Re:I don't see the goto

djberg96 on 2004-10-30T02:27:32

First of all that is written in C, not Ruby.

I said is was a C extension for Ruby. I showed you the frontend. The C code above is the backend.

Secondly I don't see why you aren't using while to get the infinite loop rather than goto.

I suppose I could have done while(1) or something. It just didn't feel right.

Thirdly you can save some lines by not always using braces.

I hate that, sorry.

Re:I don't see the goto

btilly on 2004-10-30T02:54:41

1, 3. Fair points.

About the second. I'd suggest re-reading Go To Statement Considered Harmful. The fact that your indentation and control structures are in conflict makes your program's flow of control harder to understand upon first reading. Even if you aesthetically prefer a goto to an infinite while loop, I'd suggest that you indent from the label to the goto so that that section jumps out at the maintainer.

Re:I don't see the goto

djberg96 on 2004-10-30T03:26:11

I'd suggest that you indent from the label to the goto so that that section jumps out at the maintainer.

Roger that. :)