[sane-devel] Scanner buttons (kind of RFC)

Matto Marjanovic maddog@mir.com
Thu, 12 Sep 2002 15:50:38 -0400

 >You ask the wrong question. The people do not close their frontend and this
 >would make it impossible for the others to scan when the device is locked.

Man, Oliver, you must have a lot of very cranky and forgetful people where
 you work; this seems to be a serious problem for you!

It seems to me that there are two separate issues here:

  a) Multiple users may need to be able to share one device.

  b) A backend may need to be able to keep track of the device state
      over multiple scans.

With regards to (a):

 Your solution doesn't really fix the problem.  The problem is that you
  don't want one user of the device to be able to inadvertently lock out
  other users.  However, as long as the backend is allowed to have exclusive
  use of the physical device for longer than the scope of a single sane_*()
  function call, then this is a possibility.

 Why?  Because it then depends on the behavior of the frontend.  Here is a
  simple scenario:  User A starts a long scan, saving to an NFS mounted disk.
  User A gets bored, leaves for lunch.  A few minutes later, NFS server fails
  silently somehow, and the frontend is stuck waiting to write disk, between
  sane_read() calls.  User B decides to scan something, discovers the scanner
  locked and User A gone, and then he comes screaming into your office.

 Ok, it's not a very probable scenario, but the whole point is that, given
  any device locking at all, the responsibility for ensuring cooperative
  behavior lies in the frontend --- or rather, lies outside the backend.
  Because nothing you can do in the backend will prevent someone from writing
  a frontend which hogs the device.

 So, yes, it is *convenient* for a backend to release the device between
  most API calls, but that just allows us to ignore the problem, not fix it.
  A true fix would involve some layer that arbitrates between different
  clients, and that could aptly go into a network daemon.  (See far below
  for additional commentary on this.)

Now, with regards to (b):

 If a backend needs to keep track of device state over multiple scans,
  then it needs to be able to guarantee that it has exclusive access to
  the device for the entire session during which it is controlling the
  device.  This is not inherently an unreasonable request.  The backend
  is a device driver after all, albeit operating in user-space.

 Now the question is how you define a session, and how the API's abstraction
  of a "device" coincides with the requirements of the actual devices.

 There are two models on the table:

   1) The session lasts from sane_open() until sane_close().

   2) The session lasts from sane_start() until sane_cancel(), except for
       certain cases (e.g. autofeeder source) in which is lasts over a
       multiple number of sane_start()/sane_cancel() cycles.

 How do these translate into the API specification?  Here are some options:

   Model 1, version A:  The backend is *required* to lock the device
                        between sane_open() and sane_close().

   Model 1, version B:  The backend is *allowed* to lock the device
                        between sane_open() and sane_close().

   Model 2, version A:  The backend is *allowed* to lock the device
                        between sane_start() and sane_cancel(), possibly
                        with nested sane_start() and sane_cancel() calls
                        if the source is an autofeeder.
                        (But, this time should be made as short as possible.)

   Model 2, version B:  The backend is *allowed* to lock the device
                        as long as it needs to maintain consistent state.
                        (But, this time should be made as short as possible.)

  (1B) is the simplest:  it doesn't really say anything.  It makes no
  guarantees to the caller/frontend, but does indicate that the device
  may be locked at some point.  Almost equivalent to what the spec already
  says about this, which is nothing.

  (1A) is also simple, and it also guarantees that the caller/frontend has
  complete control of the underlying device for as long as the caller keeps
  the backend open.  Such a guarantee is useful for implementing access
  control layers on top of the backend (or multi-user arbitration, etc),
  but is just as well established by making the device available to only a
  single system-daemon which does the extra control.  (I can also see the
  possibility of some oddball backend for an oddball device where it is
  not actually possible to open the device exclusively, in which case the
  backend could not make this guarantee by itself anyway.)

  (2A) is, in terms of requirements and guarantees, equivalent to (1B).
  The frontend doesn't really know when the device is locked.  Also, there
  may be devices with a need to maintain state longer than one scan cycle
  for reasons other than an autofeeder.  For such devices, the natural
  definition of a "session" conflicts with this model.
  (2B) is, in terms of requirements and guarantees, equivalent to (1B).
  The frontend doesn't really know when the device is locked, and the backend
  can lock the device whenever it wants/needs to.


After all this thinking and writing, I actually come to think that:

 o The spec is just fine as it is --- perhaps with a (1B) statement
    added to clarify that a client cannot rely on the backend to keep the
    device either locked OR free.

 o Any system that needs to ensure some combination of mutual
    exclusion or mutual cooperation should make the device accessible
    only to an intermediary daemon which takes care of such functions.
-matt m.

PS:  responses to other points, for those of you still awake:  :)

 >>         By skipping calibration later, the backend saves time; but if the
 >>         scanner has become calibrated for the wrong mode, it locks up.  The
 >>         backend thus has to maintain control of the entire session, since
 >>         it can't read this state from the scanner.
 >Also no problem.  When you start a new session (the device is not
 >locked when sane_start is called) then you have to do a new
 >calibration. In general it is senseless to skip calibration for
 >longer than e.g. half an hour because the light of the lamp does
 >change in this time so it is necessary to do a new claibration.

Suppose, as it happens for my Microtek scanner, that the calibration takes
 5 seconds.  And suppose I want to do fifty small scans, that each take
 about 15 seconds.
If the calibration is done before every scan, this job will take me 1000
 seconds = ~17 minutes.
If I can have the scanner do the calibration just once, this job will take
 755 seconds = ~13 minutes.  That saves me 4 minutes (25%).  And it is a
 lot quieter, too!

 >> An idea, perhaps for SANE2:  a fancy saned could actually maintain a queue
 >>  of clients waiting to use the scanner.  This would be great in a large
 >>  multi-user facility.  If multiple users try to claim the same scanner,
 >>  the requests line up (with a "pending/waiting/in-use-by-X" message given),
 >>  and when the scanner is released by the last person, it becomes locked by
 >>  the next person in line.
 >This also does not solve the problem:
 >When the first one who locked the device does close the frontend then the
 >next one gets access but he may be at lunch when he gets access. This
 >does not solve anything.

That same fancy saned could include a time-out:  if no commands are received
 for 5 minutes, the user is booted off the scanner, and the next user gets
 her turn.  Heck, there could be a "maximum turn time" parameter so that
 no one gets to hog a machine for more than, say, 20 minutes anyway.

 >I know a lot of people who open several instances of xsane on the same
 >machine to use different settings.

In that case, you should make it easier to save/load sets of device settings
 in xsane, so people don't have to keep 8 separate xsane windows open all
 day long.