First impressions of Ruby
Backed up from a local Blogger export (114011635902189744/114011635902189744.html) on 2026-01-01.
And so begins some adventures with Ruby…
I downloaded the tarball to my WinXP laptop, unpacked it and tried building with the Visual Studio 2003 Toolkit (free C++ build environment) and the XP sp2 Platform SDK. I ended up with a link error __ftol2 was missing. It seems to be related to automatic integer to float conversion, you can turn it off with ‘/QIfist (Suppress _ftol)’ in emBedded VC++ 3.0. I found another post in a msdn forum about watching out for different compiler/library combinations so I decided to go scrub my PATH. Sure enough I see that I have VC98 tools on my path as well as VC2003TK. I decided to clean build using the old MSVC6 compiler tools and my problem went away. I may try to rebuild using the newer compiler and libraries but it works well enough now for me.
After installing ruby and adding it to my PATH I started translating a UML sketch I had in my head from earlier into some ruby code. The idea is just a prototype for a virtual interaction model. Within an environment you generate a state for each unique interactable object. An object may be linked to multiple states to represent samples within a timeline with one state being the ‘active’ state for a given ‘view’ of the environment at any particular time.
The view window may be moved freely over the environment to select the current active ‘state’ for each object in the environment. Interaction in the environment is handled by a Proxy which holds both a command stream and a state stream and is wired to a client. The remote client sends commands to the proxy and reads states. The environment reads the commands from the proxy and updates states. Anyways it’s a pretty generic idea but just something I wanted to do some ‘sketching’ with in a free form programming environment.
I had very little experience with ruby entering into this so I was looking up things like, how to define object attributes, write methods, iterate and specify control blocks as well as constructing objects. I got a skeleton system running in no time flat without even really knowing the language, color me impressed. Once the basic system was up and spewing stuff in a meaning full way to the console I set about filling in the details.
The next step was to build a UDP client and server class and tried to run them on different ‘Threads’ in the same process. This was a mistake. Ruby has an interesting threading model, one which I can appreciate the implications of having written my share of multithreaded. The ruby runtime is exclusively single threaded, it doesn’t take advantage of native threads, instead it simulates threads within the virtual machine. From an implemenation standpoint this makes a lot of sense. The ruby runtime doesn’t have to manually synchronize any of it’s internal shared data structures to protect from multiple thread access. It’s just plain easier and more reliable to implement things this way. If you know the sync/half-async pattern or understand how an OS kernel/scheduler is designed you can invision the ruby runtime this same way. The Ruby executable is the kernal on which you may have multiple environments (stack, instruction pointer, registry file and ??) running at the same time. Co-operative multi-tasking primitives are provided for your ruby code.
Well this is all fine and dandy for most applications but the abstraction is leaky. Anytime you call out to an OS procedure that blocks you freeze the entire ruby engine. So even if you create multiple threads to read and write data on a socket you’re not going to realize the benefit of those threads because one thread that calls socket.recvfrom() is going to block the other thread from even calling send() or any other thread for that matter that wants to get some useful work done. So this sucks because it means to due significant server work you can no longer use the ‘easy way’. I don’t have a complete answer for this yet but here’s what I think are my outs right now:
- Use non-blocking sockets. There is a module named IO-Reactor that sounds promising.
- Write an async extension module for ruby using win32 IOCP CreateIoCompletionPort(), GetQueuedCompletionStatus().
I’ll try reactor first as it seems straight forward. The idea of using IOCP in an object oriented scripting language seems really cool however. While considering this I had some initial thoughts.
Would it be possible to use injection to put it into the IO module/class heirachy? IOCP is useful for monitoring sockets and files so why not expose this in the base IO library on windows.
For truly asynchrounous handling I imagine something like an internal event queue in the ruby runtime. An event dequeued by the runtime will cause the current thread to yield and passes control to a block that was registered with the IO operation and passed as an argument on the event.
The simpler approach is to just expose the blocking read from the completion port directly. You would multiplex your IO here but you still run into the problem then calling this read will freeze all your worker threads. You can always do a non-blocking read by passing timeout of 0 to the GetQueuedCompletionStatus() routine.
The hybrid approach is to service the port in an external thread and serialize these via a queue that the ruby runtime method reads but may never block on. One benefit of this approach is that you can take advantage of the optimization of servicing a single port on multiple CPUs. Another benefit is that you could provide a higher level message based API that essentially handles spooling multiple completions into a full message to cut down on the thunking cost of having to service every completion from ruby.