[sane-devel] SANE V2
Matto Marjanovic
maddog at mir.com
Tue Dec 10 18:03:00 GMT 2002
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.
More information about the sane-devel
mailing list