Hi, As an aside, some discussion around this was had before. There is an issue in the SANE standard repo where we might decide on something:
https://gitlab.com/sane-project/standard/-/issues/7 Cheers, Ralph On Wed, Dec 8, 2021 at 10:23 AM Paul Wolneykien <[email protected]> wrote: > 3 декабря 2021 г. 19:00:25 GMT+03:00, Paul Wolneykien > <[email protected]> пишет: > >В Fri, 03 Dec 2021 10:34:05 -0500 > >[email protected] пишет: > >> A callback mechanism provides a framework that can naturally avoid > >> missed button events, and can support variations such as capture of > >> "button down" and "button release" events without a requirement for > >> the program with physical access to the hardware to maintain state > >> information until a suitable poll operation is received. > >> > >> A callback design is better able to support multiple > >> buttons. Applications have less need for complex polling operations > >> (three buttons... which was pressed first?) > > > > Then, I'll try to experiment this way with my scanner. > > I think we have a problem with concurrent SANE access. > > First of all, the USB abstraction in SANE seems to be thread unsafe: > at least the USB read timeout is set separately from read with > sanei_usb_set_timeout() while the actual reading is done with > sanei_usb_read_*(). With libusb_timeout being a global variable in > sanei_usb.c I don't understand is it possible to work even with two > different devices (scanners) from the same app in parallel? > > And what about concurrent calls to sane_control_option(), > sane_start() related to the same device from the same app? > It might be possible to synchronize (enqueue) these calls with > saned daemon, but in the simplest local-running case they seem to be > unsynchronized... Is that true? > > Secondly. With the notification callback scheme we've discussed > earlier (above), it's important not to make the notification calls come > from "nowhere" (a separate thread spawned in the backend). That was > really wrong. The scanning application should explicitly maintain all > the threads that can call its own functions. And there is a known > solution to this problem in libusb: the libusb_handle_events*() set of > functions. However, this approach is really a lighter version of > polling: a polling of the internal state without sending any packets > over USB. We can call this a "software polling" meaning no regular > signals are sent over the wire. > > So, if we would even try to add a sane_handle_events() in SANE it > would still be a polling solution, a solution requiring polling > procedure in a single-threaded frontend as we can't safely access two > USB endpoints in parallel with SANE. > > Then, a question arise: can we get a software polling with the > present SANE API? And again I think we can. > > With adding the new option capability > > + #define SANE_CAP_EVENT > > we can mark options which state is safe to be asked as frequently as > you want. Technically this can be achieved by using > sanei_usb_read_int() on scanners with interrupt endpoint for buttons: > that function doesn't sent anything to the device but just checks the > state of the internal queue for the presence of any already received > data (that is pushed by the scanner to the PC). > > Moreover, reading a SANE_CAP_EVENT option shouldn't just return a > "now" value --- it should return the next available value from the > event queue, as a number of events could have been received by the > time of the read. It should not be "the button is down (up) now at > the moment you ask" as it is now with button options. > > Having an event queue is the useful approach to work with events, > but only if you have a way to reset the queue and start over: i. e. if > you can throw away meaningless events from the past. And that's also > can be naturally done with the present API: invocation of > SANE_ACTION_SET_VALUE against the event option should do the trick. > > What about the network (remote scanner) case? I think, that in that > case, a regular polling of SANE_CAP_EVENT options can be done by saned > daemon itself, with some reasonable (and configured) period. In order > to keep it single-threaded the daemon should stop this polling on > reception of a command from the remote frontend (and then, to start it > over being idle again). Then, in the case of an event being read the > daemon should immediately pass it further to the frontend's network > client part, where it should be appended to the internal queue. The > frontend polling procedure would work with that queue under the hood > and wouldn't pollute the network with "read-read-read" commands. > > Actually, the network pollution is still possible with that scheme > but only with the packets coming from (!) the remote scanner to PC --- > from some mad scanner that is trying to DDoS us! :-) > > Any comments about this update? > >
