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]> =