Gooooood morning, >If I understand correctly what happens in the case of auth_callback, >the remote sane_func isn't finished yet, the callback is still running. > >> o Network backend calls the client frontend's callback with string, >> then finally returns from "sane_func()" request. > >First the net backend calls the RPC for sane_func again, saned returns >from the clalbach, the remote frontend returns from sane_func() and >give the result back all the way to the local frontend.
Yeah, this/that occurred to me later last night. o When the device backend invokes the callback, saned can return an early response to the client backend "I'm not done with the last operation, but please invoke a callback for me." o The client net backend invokes the real callback, and then sends the return status to saned in another RPC call "Here is the result of the callback; now finish what you were doing.". o Saned returns the result to the device backend, and everything keeps on going as before (i.e. eventually the backend returns from whatever sane_func(), and then saned sends a closing response to the client). Is this how it is implemented? (I took a quick look at the net backend, and discovered that a quick look is not enough.) Neither the current SANE standard nor the v2 draft say anything about callbacks. The big question I have is: How is an asynchronous sane_cancel() invoked??? [getting back to verbose errors...] ... >As far as I understood the comment about thread-safety (from David >Mosberger-Tang) is about interface definition, not implementation. ... >So I guess the following won't hurt: > >void >sane_verbose_error(Sane_Handle h, SANE_String * verbose_message, > SANE_Int lenght) > >The the frontend can decide what to do with the string, e.g. print it >later. I don't see how it helps, and I think it does hurt: a) This doesn't have any impact on thread safety: this function still cannot be called while another sane_func(h, ...) is executing --- that other function might be in the middle of rewriting the backend's "verbose message buffer". Furthermore, without the requirement that this function is called synchronously, it becomes ambiguous which error it refers to. b) Now the frontend has to preallocate a buffer to receive the message, which will probably only be read once. How big should that buffer be? How will the frontend know if the message has been truncated because its buffer is too small? In the original version, if the frontend wants to keep the string around after the next sane_func(h, ...), it can just strdup() it. Hmm... you know what, we've overlooked something. What about a verbose error message for those functions which don't require a device handle? (back to this in a moment) ... >As far as I understood the comment about thread-safety (from David >Mosberger-Tang) is about interface definition, not implementation. > >So even if the current implementation doesn't allow simuktaneous >operation of SANE function, a later one may do. And we are in trouble >then. All the other function definitions seem to be thread-safe. Ok, this is worthy of some discussion. I don't see any current guarantees of thread-safety anywhere in SANE. On the day that we amend the standard to say "these functions may be called simultaneously", we will all be screwed anyway, because none of the backends will have been written with thread-safety in mind. We need to explicitly define which operations can be called at which times. Here is some brainstorming: First, let's consider the device-dependent functions: SANE_Status sane_control_option(H, ) SANE_Status sane_get_parameters(H, ) SANE_Status sane_start(H, ) SANE_Status sane_read(H, ) void sane_cancel(H, ) SANE_Option_Descriptor * sane_get_option_descriptor(H, ) void sane_verbose_error(H, ) void sane_close(H) These all act on a particular device handle, H. It should be perfectly safe to call these functions simultaneously and reentrantly for different handles, but not necessarily for the same handle. The first four are all synchronous operations --- it doesn't make any sense to call any of them simultaneously, for a given handle H. (E.g. You can't get the scan parameters while you are in the middle of setting an option, and vice-versa.) sane_cancel() is signal-safe, and might as well be thread-safe, too. One should be allowed to call sane_cancel() at any time. sane_get_option_descriptor() returns a pointer to a descriptor, and that pointer is guaranteed to be valid as long as the device is open. So, technically, this function could be thread-safe and signal-safe, too, since it is just returning pointers which are presumably allocated once during sane_open(). However, the values of those option_descriptors --- the stuff that is *pointed to* --- is not guaranteed to be valid for the duration of a call to sane_control_option(). Setting an option may change the ranges/etc of other options. So, the option_descriptors themselves must be accessed synchronously, more or less, for a given handle H. sane_verbose_error(), as we've discussed, reflects the return status of the last synchronous operation, hence it must also be called synchronously (for a given handle H). sane_close() invalidates the device, so it also must be called synchronously. It is probably be a good idea to declare that it is illegal to call sane_cancel(H, ) during a call to sane_close(H, ). Ok, now the device-independent functions: SANE_Status sane_init() void sane_exit() SANE_String_Const sane_strstatus() SANE_Status sane_get_devices() SANE_Status sane_open() sane_init() and sane_exit() are definitely synchronous -- one has to be called before anything else, and one has to be called after everything else. sane_strstatus() should be completely thread and signal safe. It is not even a backend function, technically, and just returns a string constant. sane_get_devices() appears to be synchronous, given this language in its description (4.3.3): "The returned list is guaranteed to remain unchanged and valid until (a) another call to this function or (b) a call to sane_exit() is performed." Thus, a second thread calling this function could silently invalidate the list being accessed by the first thread. sane_open() looks like it *could* be made re-entrant; there doesn't seem to be anything about the code-flow the prevents it. But this may require some mutex mechanism in the backend. However, sane_open() should be declared thread-safe with respect to the device-dependent functions above. I don't see why sane_open() would ever need to be called more than once simultaneously. But I can imagine multi-threading being used (in a network daemon, for example) such that there was one thread assigned to each open device, and a master thread in charge of handling new device requests. The master thread would be the only thread that ever needed to call sane_open(), once at a time. Hmmm... so, back to sane_verbose_error(H, ): sane_init(), sane_open(), and sane_get_devices() all return a SANE_Status, and for the first two, at least, one might want to see a more verbose message/explanation from the backend. If these functions are all required to be called synchronously, then we could simply say that sane_verbose_error(NULL, ...) returns the "most recent global verbose message". That's what I got so far. Ideas? -matt "get it in writing" m.