Hi,
I''m using EventMachine timers in sort of a weird way, so I figured
I''d just mention what I''m doing in case there might be ways to
improve upon it, etc.
My application is game-like, and consists of a thin client (C/C++ only)
which is essentially a window server + renderer, talking over loopback
to the main engine/database process (ruby-centric).
I''m using EM on both the ruby engine/database process, and the C/C++
window server + renderer process. (As an aside, I needed to make some
tweaks to the EM code to get it to compile in sans-Ruby mode on Windows.
Diffs below, if of any use.)
Anyway, I was considering how to integrate GUI events into EM,
but I realized my application is game-like enough that it will be
rendering frames at a more or less constant rate. So I turned the
problem inside out, and set the EM timer quantum to the framerate
I wanted. Then I repeatedly install a one-shot timer, to fire
"immediately" (effectively at the next interval defined by the
quantum.)
So, my initialization looks like this:
evma_initialize_library(event_callback);
evma_set_timer_quantum(16/*msec*/); // roughly 60Hz
evma_install_oneshot_timer(1/*msec*/); // i.e. "fire at next
quantum"
evma_run_machine();
And my callback handler deals with any pending GUI events, and
sets itself up for callback at the next quantum interval:
static void event_callback (const char *a1, int a2, const char *a3, int a4)
{
GLWindow::pump_events();
// TODO: update physics, render new frame, SwapBuffers
evma_install_oneshot_timer(1/*msec*/);
}
Now, I must admit I''m not sure this is better than REALLY
turning the whole thing inside out, and having my main loop
control the timing, and just invoke some public version of
EventMachine::RunOnce() every frame.
If I made EventMachine::RunOnce() public, I could have a
more typical game loop:
- handle GUI/input events
- handle network events (EventMachine::RunOnce())
- update physics
- render new frame
- SwapBuffers (possibly delaying until next vblank)
But I guess either way will work about the same in practice.
(Although, to achieve a constant framerate, I may have to
dynamically adjust the quantum... Subtracting out the time
taken by SwapBuffers as it will be delaying internally for
the next vblank, if the user has wait-for-vsync enabled.
(Now that I think about this, I''m leaning more toward wanting
to call RunOnce() from my own loop... hmm...))
In general, I guess an appliction centered around producing
successive game frames at some (relatively) fixed interval,
is sort of at odds with the normal EM concept of handing
events at whatever moment they arrive.
But anyway... Francis do you have any thoughts on this, for
instance regarding my using the quantum to define my
framerate timer, vs. making EventMachine::RunOnce() public?
I suspect if EventMachine::RunOnce() were public, I would have
gone that route instead of manipulating the timer quantum.
Thanks,
Regards,
Bill
P.S. Here are the aforementioned diffs. Mainly I had to tweak
some stuff so that WinSock was initialized before the LoopBreaker
socket was created, and change the "close()" of the LoopBreaker
to "closesocket()". Also, I removed evma__write_file() as it
didn''t appear to be implemented yet, and Windows doesn''t have
an O_NONBLOCK mode with the stdlib file API anyway. :-(
(NOTE: Out of habit, I changed "winsock" to "winsock2";
however,
in retrospect I didn''t really have a concrete reason for doing that.)
svn diff
Index: em.cpp
==================================================================--- em.cpp
(revision 284)
+++ em.cpp (working copy)
@@ -51,6 +51,15 @@
LoopBreakerReader (-1),
LoopBreakerWriter (-1)
{
+ #ifdef OS_WIN32
+ WSADATA wsadata;
+ int res = WSAStartup(2, &wsadata);
+ // if (res) {
+ // fprintf(stderr, "ERROR WSAStartup: err=%d",
GetLastError());
+ // return false;
+ // }
+ #endif
+
// Default time-slice is just smaller than one hundred mills.
Quantum.tv_sec = 0;
Quantum.tv_usec = 90000;
@@ -77,8 +86,8 @@
for (i = 0; i < Descriptors.size(); i++)
delete Descriptors[i];
- close (LoopBreakerReader);
- close (LoopBreakerWriter);
+ closesocket (LoopBreakerReader);
+ closesocket (LoopBreakerWriter);
}
@@ -197,8 +206,9 @@
void EventMachine_t::Run()
{
#ifdef OS_WIN32
- WSADATA w;
- WSAStartup (MAKEWORD (1, 1), &w);
+// %%BWK ... hmm, this needs to happen earlier, before the LoopBreak UDP socket
is created
+// WSADATA w;
+// WSAStartup (MAKEWORD (1, 1), &w);
HookControlC (true);
#endif
@@ -363,7 +373,15 @@
* and send a loop-break event back to user code.
*/
char buffer [1024];
+ #ifdef OS_UNIX
read (LoopBreakerReader, buffer, sizeof(buffer));
+ #endif
+ #ifdef OS_WIN32
+ struct sockaddr_in sin;
+ socklen_t slen = sizeof (sin);
+ memset (&sin, 0, slen);
+ int r = recvfrom (LoopBreakerReader, buffer, sizeof(buffer), 0, (struct
sockaddr*)&sin, &slen);
+ #endif
if (EventCallback)
(*EventCallback)("", EventMachine_t::LOOPBREAK_SIGNAL,
"", 0);
}
@@ -817,6 +835,7 @@
EventMachine_t::_OpenFileForWriting
***********************************/
+#if 0
const char *EventMachine_t::_OpenFileForWriting (const char *filename)
{
/*
@@ -836,6 +855,7 @@
return fsd->GetBinding().c_str();
}
+#endif // 0
/**************************************
Index: project.h
==================================================================--- project.h
(revision 284)
+++ project.h (working copy)
@@ -68,12 +68,16 @@
#ifdef OS_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-#include <winsock.h>
+#include <winsock2.h>
#include <rpc.h>
#include <fcntl.h>
#include <assert.h>
typedef int socklen_t;
+#ifndef BUILD_FOR_RUBY
+// #include <io.h>
+// #define O_NONBLOCK 0
#endif
+#endif
using namespace std;
Index: emwin.h
==================================================================--- emwin.h
(revision 284)
+++ emwin.h (working copy)
@@ -24,7 +24,11 @@
*****************************************************************************/
+#ifdef OS_WIN32
+// #include <WinSock2.h>
+#endif
+
// THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY.
// INCOMPLETE AND DISABLED FOR NOW.
#ifdef xOS_WIN32
Index: cmain.cpp
==================================================================--- cmain.cpp
(revision 284)
+++ cmain.cpp (working copy)
@@ -224,12 +224,14 @@
evma__write_file
****************/
+#if 0
extern "C" const char *evma__write_file (const char *filename)
{
if (!EventMachine)
throw std::runtime_error ("not initialized");
return EventMachine->_OpenFileForWriting (filename);
}
+#endif // 0
/********************************