January 6th, 2004

  • evan


Note: I often write these sorts of posts while I’m thinking about the problem. Writing it out often helps me decide on a good way to solve it.

The hip thing these days is to provide an XML-RPC API to your webapp. The idea is that clients can easily map the RPC functions directly to real function calls so the app can call them as if they were locally available. This is more or less what I’ve done for LogJam’s Blogger API support: I have a utility+library that autogenerates my C bindings. But at least for C, this trick breaks down.

It comes back to blocking: network calls take a while. Blocking in a GUI is fatal, as I’ve written about before [see particularly this comment].

So, you might think, just put the call to your RPC wrapper function off in its own thread/process and be done with it. But if you report progress in some centralized/abstracted way, C’s lack of closures gets in your way. What you’d like to do is have some function like run_with_progress(func, args) that handles all of the bookkeeping to run func while keeping your app responsive and the user informed.

But the whole point with these RPC wrapper functions is that they don’t have a homogeneous argument list! There is no correct signature for run_with_progress that has the right form of args. You’re back to passing around structs that collect the arguments, at which point there wasn’t ever really a point in even pretending those remote RPC functions behave as local functions; you’ll need wrappers around the wrappers that take in collected-argument-structs and call the functions.

What are the blue-sky solutions?
  • Closures would help sorta like this: OCaml’s GTK bindings have a few functions for working in multiple threads simultaneously. They provide a “sync” function with the signature ('a -> 'b) -> 'a -> 'b. (That is, it takes in a function and the argument to pass it and returns its result, but it makes the call happen in the main thread's context.) Similarly, to call some complicated RPC-wrapped function, I might make a call like run_with_progress (fun _ -> rpc_function username password etc) (). (For you non-ML heads: "run_with_progress(sub { ... });".)
  • Or, turn around the problem: if the event-handling of the window was somehow handled outside of the window code, then blocking would be ok. This is handled to some extent on both Windows and X—notice how when an app locks, some of the widgets (or on X, some of the colors) continue to repaint?
    But I don't think this is a full solution. Consider a widget with custom (aka, application-provided) drawing code; if you move another window over it (or do anything else to cause a repaint) then it won't be repainted if your app is blocked.

In conclusion, we really oughtn't be using C for non-system-level applications. But we all already knew that. :P