Fabio Alemagna <[EMAIL PROTECTED]> wrote:

O.K. - seems like we are finally getting somewhere. Thank you for your
patience.

The thing that annoyed me was basically to insist on automatic 
backbuffering and that I was in the impression, that you wanted to have it
done synchronously. Now I see quite some merit in your proposal, and
now let's see if and how it can be integrated with the internals of LibGGI
...

Thank you for bringing back some of my ideas of old that I gave up on
for some reasons of which not all where true seemingly.


> The general rule is that a shard HW resource has to be acquired in order
> to use it, and released when done.

O.K. This would have to be done anyway in most cases. Might be a small
problem with some technical details, as we might need locks accessible 
from both kernel and userspace and maybe some "special kinds" of locks
(see below), but that should only be a technical issue.


> If an application requires access to a resource, it tries to acquire it,
> and if that resource is already acquired in an exclusive way by anoter
> application then it will have to wait for the other one to release it.

O.k. - though I'd say that in some cases we shouldn't wait, but rather
fail. Also see below.


> When VT switching (or screen switching on AmigaOS) happens, the
> application that is going to be switched to the front, transparently and
> trough the help of the framework, will try to acquire possession of the
> screen and will wait until such acquisition is possible.

O.k. Assuming the locks are held as briefly as possible by the application,
this is good. As there might be multiple locks (on framebuffer, on
accelregs, on some asynchronous kernel-side mechanism, ...) involved we 
should present that on a higher level in the application, which we 
basically already have using the ggiResourceAcquire functions.

Moreover I think we should have kind of a "local lockout" type lock for
accessing the graphics card at all, which is local to to application.

This allows the kernel to set that lock for an application that gets 
switched away.


> 1) The application uses an API to draw on screen (putpixel, drawline, and
>    so on). Each API's function has to acquire the video surface in order
>    to draw on it, and all other applications trying to draw on the screen
>    would have to wait for the current application to exit the current
>    function first. 

Well ... - partially yes. Think of multithreaded applications. There are
some cases, where you need to wait (when drawing using the accelerator 
e.g.), while in others you don't need to (when drawing via framebuffer). 

However that can be solved by using counting locks that allow the current 
task (not thread) to lock them multiple times as long as one frees them
accordingly.

That might open the possibility for deadlock WRT to switching away 
(multiple threads locking the Fb in an overlapping pattern), though. 
But see at the very end for a possible solution.


>    Since the application uses the API to access the framebuffer, VT
>    switching can happen in between one API call and the other one. 

Not necessarily. But as you mention below yourself, the exception is 
direct buffer access. LibGGI pprovides for locking these, so a well behaved
application will just look as if it has "implemented a LibGGI primitive"
inside such a block. No matter what the buffer does. The application can
even safely access other such areas (even accel regs), as long as it locks
just as the lib would do.


>    2.1) Just like in any other case, the switching won't happen until the
>         application releases the framebuffer. Well behaved application
>         won't keep possession of the framebuffer for too long anyway, and
>         in case they do - AND ONLY IN THAT CASE - such applications will
>         be put to sleep. It can be done by setting a timeout on the
>         resource acquisition - say 5 secs. 

Yes. Though I'd rather use the "double-tapping" principle to give the
user more control. But maybe it should be limited on both ends, i.e.
the app should be given a minimum of say 1 sec and a maximum of say 
10 secs. The former to give it a chance, if the user happens to 
_accidentially_ press the key twice, the latter to allow for automated 
VT-switching using chvt or similar to succeed.


>         If the time goes up, the
>         resource is temporarily released and the faulty application is
>         put to sleep. This approach is user-friendly because
>         in the majority of cases the application won't keep the resource
>         allocated for so long.

Yes. As long as the locks are basically application local, that sounds like
being easy enough to do, as it is not needed to "break" the locks then,
but one rather set's flags in some kind of "application descriptor"
that "this app is halted" and may not be scheduled.

I suppose the scheduler has provisions for that.


The only issue here is, that the application may have an inconsistent
internal idea of the framebuffer as compare to the real contents if such
a forced switchaway happens. 

Reason: If we break away while the application is in a critical block
accessing the accel regs, we might not be able to save the state of the
accel engine in enough detail to have it continue exactly where it left
off.

For direct framebuffer access, this could be remedied by just transparently
replacing it with RAM at the same address, but not for other buffers.


>    2.2) The other approach is the safest one, and the most userfriendly
>         one: use the MMU to fake a framebuffer where it's not: no need for
>         timeouts at all.

Here I slightly disagree, as already hinted above. What about an 
application that is stuck inside a _non_framebuffer_ locked area. 
Think of it like an attacker would do:

Try to send SIGSTOP to the application, while it is in a critical area
(no matter if the app or the lib has teh critical area).
Now switch consoles. If we do not catch that case (by timeout or similar), 
the machine is locked.

Sure, this ain's much of a real "attack scenario", because the attacker
would need local console access, but if an attacker can do it, an 
accident can as well. And accidents should not happen on real OSes -
that's what Windows is for ;-).


> However, I also suggest other methods in addition to this one, which
> include notiification of switching to he application. Notification,
> however, is asyncrhonous and the framework won't depend on an answer from
> the application to continue its job: that would open to DOS.

That is good. As detailed above, I think DoS is still possible, thus I'd say
we need a possibility to forcibly switch away.


> As I wrote in another email, the application can tell the target that,
> when it loses visibility, it wants to either
>     1) Continue running on a backbuffer
>     2) Continue running without backbuffer
>     3) Be stopped with a backbuffer
>     4) Be stopped without a backbuffer
> 
> Andreas rejected this approach because he said that if we are to list
> things that an application can request to the framework then why not
> include _everything_ in the list? Of course since that is impossible
> Andreas suggested that no list is used altogether, and that the
> application has to be always notified - synchronously! - and handle the
> matter itself.

The "synchronously" is a derivative from my approach to try to keep stuff
very simple. It isn't actually synchronous (can't be) to the switching
commands (oh - just seen your definition below - 100% right, then), 
but let's see what we can agree about:

1. I agree the application should be notified.

2. I don't quite feel comfortable with the application "registering" some
behaviour. O.K. - using the "Continue running" option, we are back at
being able to do what we want. So this is no big deal. I'd rather KISS
and just let it always run on (unless on forced switchaway) and then
let the app decide to sleep, if it wants.

3. I don't feel comfortable with backbuffering at all. especially not
if we have to do it in the scenario you depict above.


Let me detail on 3. a little:

1. If we _guarantee_ backbuffering, we should _really_ be able to 
   _guarantee_ it.
2. Given your approach, we will have to be able to forcibly switch away
   anyway. In that case, we cannot really safely reconstruct the 
   framebuffer contents. O.k. - we could declare that the "paranoid" or
   "broken application" case, where it would be acceptable to have some
   data loss.
3. If we want to be able to continue running, it is much easier to
   implement, if we accept desychronization of the application's idea 
   of the framebuffer contents and the real contents while we are
   switched away.

Reason: Look at that pseudocode:

        Lock(hardware-regs);
        draw_something_using them();
        Unlock(hardware-regs);

        calc_something();

        Lock(hardware-regs);
        draw_something_using them();
        Unlock(hardware-regs);

This could well be part of a single LibGGI (or rather some higher level
extension) primitive.

Now if a switchaway happens in calc_something(), we will not be able to
acquire the lock afterwards. If we block on the lock, the app would
be forced to sleep, which is not what we want.

So we have to use trylock-like semantics anyway. Now what do we do, 
if the locking fails?

The simplest thing is: just return error. The application will become
desynced from the framebuffer, but that's about it. no complex logic 
at all.

The alternative would be to jump to some emulation layer that is 
doing to the now emulated framebuffer what the drawing primitive would do.

This is not only relatively hard to do in LibGGI (to do it in an elegant 
way means switching visuals, which is hard to do from the code of a 
"to-be-replaced" visual handling function), but also requires that
the kernel takes care for copying the real framebuffer to the fake one.

Reason: 

If we just rely on the locks being all released to switch away, we can
switch away as soon as the locks are free. In that instant, we must also
lock out the application from acess. That means the application cannot
save away the framebuffer for us. 

Even if we signalled the application at that instant, to save it for us,
and have suitable semantics on the locks for that to be possible, that
would again open for DoS and require yet another timeout.

Moreover, the application might be within a primitive, that does multiple
lock/unlock calls which would be a mess to fall back from to FB-access.


Neither should the new application be able to, as it should get a cleared
framebuffer, due to security considerations.


So we have to do it somewhere else. Doing it from the kernel is pretty ugly,
doing it from userspace requires some kind of daemon handling the swapping
with a little kernel help. I had such a daemon in early versions of scrdrv,
but it's a kludge IMHO.


Thus I still opt for not providing transparent backbuffering. 

I think it is not a big loss, because either the application _can_
redraw quickly, or it takes long to draw its current scenery in the 
first place, which usually means it doesn't matter much, if it takes 
just a little longer by drawing into a backbuffer of its own
(it's really easy to do with the memtarget) as well, thus gaining the
ability to redraw at will.


> How's that possible? Very simply, indeed: the application will be notified
> by the framework of events such as "hide" and "expose". The application is
> _not_ forced to handle such events, specially if it requested to continue
> running on a backbuffer, however it should always check for the expose
> event because in some cases it might not be possible to provide backbuffer
> (very seldomly, actually, but it's a possibility), 

O.K. - so basically we agree here. And if the app cannot rely on it 100%,
I'd rather avoid the complexity of providing it in the first place.


> When the application receives a "hide" event it can take any actions it
> thinks it's appropriate to take, and some applications might want to stop
> rendering (those apps tipically won't request a backbuffer), or will want
> to put themselves to sleep by simply waiting for the "expose" event.

Ack. Though I'd like to separate that into a "visibility" and an "expose"
part. Application might change its visibility status without need for
an expose, e.g. on targets like X, for that case that backbuffering is
available and works.


> Then, Andreas says that applications will have to implement backingstore
> themselves, however I don't see why such thing couldn't be provided by ggi
> itself, so that's why there's also option (1) in my model: less hassle for
> the programmer, no need to code over and over the same piece of code just
> to make the application switch to an alternative framebuffer, when it can
> all be done automatically.

As detailed above, I'd really like to avoid the complexity brought by
backbuffering, especially as we can't guarantee it anyway.

That is not only a KGI issue, but it might also be an issue with some
braindead backend we might need to cope with someday:

One that already internally does what I propose to do above, i.e. one that
_ignores_ drawing events as soon as it gets switched away. X already
theoretically has that behaviour if no backing store is provided or runs
out.

So we have at least one target that cannot reliably provide backing store,
so we should just advise applications not to rely on it.

And if that is the case, we will save ourselves lots of trouble implementing
backing store.


O.K. - so much for that. I do see, that your proposal is superior to
what I said, and I am glad to find that I wasn't so wrong a long while
ago, before I got somehow convinced that this wouldn't really work out.

Regarding the issue of backbuffering, I don't agree (yet? :-). I know it
can be done for KGI, but I do not think it is worth the bother.


Now for a technical issue: The locks.

Let me propose a simple scheme, and see if it works out.

Say we have some graphics card ressources that can be locked by one thread,
a few that can be locked by multiple ones.

Shouldn't be a big deal, especially if these locks are application local
and only the kernel needs access to them for switching purposes.

Having the locks application local avoids lots of hassles with shm and 
other users' apps accessing that and maybe trying to DoS the current 
user.


Some of these locks might be interdependent, i.e. maybe one cannot
lock e.g. the bigendian version of the accel regs, while the littleendian
one is in use (IIRC a few cards have such a feature).
That can be easily solved as we can use ggiResourceAcquire which will be
able to map such requests to lock multiple things.


Now we still have to deal with the case, where a switchaway is pending.

Having the locks acquired externally by the kernel would make the
application block for them, as it should when they are held by another
thread.

Thus I propose these locks should be tristate: Open, Locked and LockedOut.

The LockedOut state would just cause all further lock requests to _fail_.

Unlocks should still work, so that we finally get to an "all unlocked"
state.

This requires only that we check results of all lock operations, and 
if we do not have to provide backbuffering, we would just be able to
return from the respecitive funtion, telling the caller that we
were not able to complete drawing due to switchaway.

If we wanted backbuffering, we would have to block at that point 
until all other locks are released and the fb got copied into the 
backbuffer and then we would have to switch rendering to be software 
only and continue.

Given the structure of LibGGI and the arguments above WRT to the need of
backbuffering only for the few cases where the app cannot redraw and the
possible inability to provide it for some targets, I'd rather live
with no guaranteed backbuffering.


CU, Andy

-- 
= Andreas Beck                    |  Email :  <[EMAIL PROTECTED]>             =

Reply via email to