Re: Dealing with "past" events

Nov 04, 2024 Last reply: 1 year ago 8 Replies


Given:


>N = "now"
>R = task release time
>D = task's deadline time
>E = event time
>
>The only real invariant is that R < N as code executing in a
>task can't execute until the task has been released.
>
>Specifically, there are no guaranteed relationship between
>*most* of these times; N > E, N > D, D < R, etc. are all
>possible in a generic system.
>
>Though, in a nominal setting, R < N < E < D when the code tries
>to initiate an event at a particular (future) time (E).
>
>But, what happens when E < N -- i.e., when you try to schedule
>an action (event) at a time that has already passed? And, does
>E << N lead to a different interpretation/handling?

There is no general answer to this: the only correct thing to do is let the user specify how the event is to be treated if its scheduled time is passed.


It reasonably is safe to assume that a "do it now" event should be executed as soon as possible, even if was delayed several seconds in the scheduling.


But beyond that you're speculating.


Unix 'cron', 'at', etc. are not particularly good examples to follow - they are too simplistic. The set of options available to the Windows scheduler is better (though not exhaustive), but IMO most of the "options" should be mandatory parameters that must be provided in order to schedule an event.


I've argued that the OS shouldn't special-case such activities.
>If you request something to happen in the past, then the OS
>should just act as if it has *just* happened, regardless as to
>whether you were a microsecond "late" in issuing your request
>or *years*! In particular, the OS shouldn't dismiss such request
>unilaterally -- or, throw an error to alert the issuer to the
>*apparent* inconsistency.

I think it should be an error for a /timed/ (not "now") event to be scheduled past any possible execution time. An event that repeats could be scheduled past its initial run time, but there should be at least one repetition in the /future/.


E.g., an event that repeats 5 times at 1 day intervals starting 7 days ago is meaningless now. But an event that repeats 5 times at 1 day intervals starting /3/ days ago still has potential to execute.


Arguing that an administrator might (re)set the system time and that might make a passed event relevant again is just wishful thinking.

Not the point. My comment was about what options / parameters are available to the schedule(r).

Now you're not paying attention: I suggested above to look at the Windows scheduler. One of the options (paraphrased) is "run asap if missed".

But things like that should be the user / programmer choice based on the task to be performed - not a system policy.

Again the Windows scheduler: (paraphrased) there are options to "wait <time units> for idle state" "wait until in idle state for <time units>" "stop if idle state ceases" "start again if idle state resumes" "after trigger delay execution randomly for <time units>" "remove from schedule after <epoch>" "check for network connections available" "start only on line power" "stop if on battery" "wake up to run this task" "keep trying to start for so many <time units>" "stop/abort after so many <time units>" "stop the task if it runs too long" "force abort the task if it won't stop"

and more.

There are also /schedule/ priorities[*], and the task itself can be scripted to run at a given OS priority (and as any particular user). Sub "resource" for "idle" and this list ought to give you a few ideas for what you should provide.

[*] schedule priority is not visible in the GUI. To see/modify it you need to export the task to XML, edit the file and import it to recreate the task with new settings. Yeah, Windows really /is/ a pain sometimes.

No I did /not/ miss this.

You asked how best to handle these things. The answer is provide options in the scheduler and ensure that the options are (at least) considered by the programmer by making them /required/ parameters.

If you don't like the number of parameters, pass them as a structure.

What you're describing here is meta: it's a condition of utilizing the scheduler in the 1st place, not a condition of triggering a scheduled event.

I think what you may really /mean/ is dependency scheduling ... i.e. "this event depends on there being one or more other events already scheduled".

The number and criticality of such dependencies can be unbounded - or at least unworkable to specify - and I don't think checking for them belongs /in/ the scheduler or its API. If some program depends on such meta knowledge of what is scheduled, it should have to keep track of that itself.

If all the options can be (de)selected individually, then you don't have to bundle them all together ... but then you run the risk of the programmer forgetting something.

It's a good idea anyway for the API to allow modifying an existing schedule without having to delete/recreate it (even if "modify" really is just an illusion for the programmer).

Scheduling in a complex system is NOT SIMPLE and it never has been: whole books have been written about it. The overwhelming majority of events likely will require only a small subset of the scheduler's options, but the scheduler itself must deal with the most complex case possible.

You might consider having multiple APIs [parameter blocks?] of varying complexity. But there must be some version of the API that can specify every possible option.

The problem is that the REAL scheduler /needs/ the flexibility of all of those options to do its job. You can put lipstick on the pig - ie. use a service to present a more friendly use API - but that doesn't change the underlying requirements.

The problem with that is that it ends up putting a whole bunch of what /effectively/ is - or should be - "scheduler code" into a whole bunch of applications. Instead of reducing application complexity by introducing a smart service, you instead increase complexity by forcing the application to figure out for itself when and how it can make use of a dumb service.

Where do you get that from ?

You wrote:

All you can reasonably conclude from this fragment is that the scheduler was invoked twice: first on the 1st event, then on the 2nd - and that assuming they were scheduled successfully, the 2nd event can be expected to be triggered at least "delta" time units after the 1st is triggered.

Nothing in this indicates that "delta" is intended to allow the 1st event to complete whatever it was supposed to do. In fact, nothing indicates the two events even are related.

Which is precisely the point I was making about scheduling options. You have to have at least some way to deal with the relationship. If the 2nd depends on the 1st, then the tasks must signal outside the scheduler, and the 2nd potentially must fail with a code the scheduler understands means to "try again". And then there must be options to say how long before trying again, how many times to try again before giving up, etc.

You're assuming a particular style of coding ... I don't think you can make that assumption.

Again, that is outside the pervue of the scheduler. If A wants somehow to signal B that it has completed, that has to be handled by the tasks themselves.

Unless the scheduler can know that A has completed, and know the status of A when it finished ... and make executing B dependent on that. But that is asking too much because:

I wrote previously -

If you've already made up your mind that that is the way it must work, then what the heck is this conversation about?

Say you want a task to start every day at 00, 08 and 16. You don't care that it starts exactly on time, but you don't want the ideal start time to drift with execution, so (re)scheduling as part of the task won't do.

Say you want a task that runs M,W,F at 10, and T,Th,Sa at 14. Sunday it does not run. Again, you don't care that it runs exactly on time, but it must complete within an hour and so it must start within 15 minutes of the scheduled time or it can't be run.

Are you going to run a scheduling task every day to schedule for tomorrow? What if that task doesn't get run?

Are you going to run one or more monitor tasks constantly (wasting CPU and power) to schedule other tasks?

IMO either of these are ridiculous.

You want a schedule that starts at 00, repeats every 8 hours, and (pending resources) continues to try to start for X <time>.

You want a schedule that runs M,W,F at 10, tries to start for up to 15 minutes, then aborts if it can't be started. You want a second instance of this schedule that runs T,Th,Sa at 14.

And you want options in your /scheduler/ to be able to specify these conditions.

That is confusing formulation of the problem. First, what is "the OS"? Second, your "task release time", "task's deadline time", "event time" are undefined. In particular, one meaning of deadline is "time when a task should stop, regarless if it is done or not".

If by "the OS" you mean operating system kernel, then you want it as simple as possible and move various functions to upper layers. In particular, in general purpose OS you may have timers which activate a task some time after scheduled time (hopefully close to scheduled time, but there is no warranty). Maybe you are thinking of such a timer.

However, there may be higher level "scheduler". For example Unix 'cron' and 'at' are userspace programs, but are considered part of operating system. 'cron' and 'at' are rather crude, if you may need more. In particular, an automations system typically must schedule various activities with specific time constraints.

Anyway, your system probably is intended to do something useful and that will lead to some requirements. Once you have more concerte requirements, you can think how to best satisfy them. If you have only handful of time-dependent tasks and each differs a lot from other, then putting all higher level time handling in the tasks may be reasonable. But usually it pays to have common handling. As George mentioned there can be interdependencies between task, so it is likely that you may need a higher level scheduler to coordinate them. Concerning kernel, you should have in kernel enough to support higher level code. In particular, if you have "fast" tasks and care about their time performance, then you may need real-time features in the kernel. If you do not care, than usual general-purpose approach is enough. But if you do not care, then why are you talking about deadlines?

Concerning tasks way in the past: this is very unusual case for general purpose kernel. Namely, the only likely reason for long delay is because system was turned off. But kernel timers frequently are "volatile", that is they will be gone when system is turned on again (if that is undesirable, then higher level layer may restore timers that were supposed to persist). AFAIK general purpose kernels handled timers as you argue, that is dully activated corresponding task. OTOH long delays were usually delegated to userspace which could have complicated rules.

It matters. OS kernel should implement something simple, but enough to support higher layers. At higher layers you may have something more fancy.

OK

I take this and what you write later that you think about library interface for "applications".

You can not solve _all_ scheduling problems. It does not mean that you should not solve some. Windows style interface where you have a lot of specific cases and can not do anything else than provided choices does not look attractive to me. OTOH 'constraint language' to specify various constraints (which may be combined in arbitrary ways) could go quite far. There is also time-honored approach of allowing "user" extentions (possibly up to replacing "standard" scheduler).

Again, this is question of design of your OS and your requirements. If you have badly behaving third party code, then it may be hard to give any warranty. But if you have need and apropriate resurces then there are ways to get real-time behaviour (that is hitting specified time window). Even if you do not have real-time motivation, I think that time windows (as opposed to discrete events) appear frequently. For example, you want to turn on a device during night tariff.

Anyway, if you can not give reasonable warranties for time when your scheduler runs, then it means that you do not care. By extention, you do not care when scheduled tasks run. Which may be fine for some purposes and in such case simplistic handling which you propose may be good enough.

When you wrote your original post, I had someting different in mind. Not so long ago I looked at one of home automation system. I searched for some specific info and one of first things that I found was a discussion about scheduling interface in the system. A user wanted simply looking thing "if washing machine is idle for some specified time, then turn its power off". System allowed specifying conditions which caused some actions. But there were limitations on what could be put as a condition, and in effect correctly specifingt needed thing was rather awkward.

Out-of-kernel does not mean unprivileged. Tasks may have special rights which other tasks do not have. For example one may allow elevated scheduling priority only to system tasks. Even otherwise unprivileged task may be special because it provides "well-known" interface to other tasks.

For me important question is "who is the master?". Do you allow third-party code to play any tricks it wants. Or do you give control to the user. As a simple example, task may decide to do some work at night, presumably to avoid interference with user work. But a user may prefer to work at night, say on a specific day. So user may wish to delay execution of the task. If you want to empower users, then central facility is proferable, as this is easier to control than separate tasks.

I have my own bias, I want computers to do what I want. For expample, when I make a break I prefer to have some special combination of keys to put system to sleep _immediately_. When I work, I may think for longish time, and systems that go to sleep based on "inactivity" distract me (especially when I am trying to understand a text or a picture on the screen and the screen goes blank).

I asked: what is "the OS"? In particular where you put the line between OS and applications.

You provide an interface. In modern times that is usually in form of a function call. "Applications" need not care if the actual implementation is library code or in kernel or as a separate task.

As I wrote, IMO correct place for such a functionality is inside OS, but outside the kernel.

The "nothing is common" part looks strange: it is pretty common to want triggering on edges and also time windows.

In extendible scheduler that would be just abstract bit of information and _application_ would tell its value to the scheduler. If there are dependencies something have to know about them. In particular, if you have dependence between independently developed application it is natural to have scheduling as a third party.

I did not write "malicious". Rather, something like polling in a busy loop instead of using blocking system call. Or Windows graphic drivers which did not check that hardware is ready to accept the data. Microsoft wrote that this was common because it slightly improved average throughput of the driver. But it could stall PCI bus, effectively freezing the whole computer for say some miliseconds (IIUC now such access fails rather quickly, but it used to be a problem).

"Do not care", "do not care enough" or "care more about other things" mean essentially the same here.

I wrote about "reasonable warranties". There is notion of "force majeste" and clearly power failure counts as such.

PLC-s have vendor provided I/O, so vendor knows how much time I/O will take. They also have "logic", which is stright-line code so vendor knows maximal execution time. Consequently, if PLC task is given sufficiently high priority it will run with predicable frequency. You may consider it unneeded, you may think that other features are more important. But in the end, either you care enough to provide something like this or you do not care enough. In later case there is no much point in thinking a lot about missing deadlines.

I should have been clearer. The "home automation system" consisted of devices (which ATM are of no interest) and user interface/scheduler working as a normal application on standard OS. User interface was supposed to be easy but allows users to define various actions. The point is that in making it "easy" (and probably simple to implement) user interface got crippled so that resonable thing was hard to do.

AFAIK the system was open-source, so theoreticaly third parties could add any improvements they wished. But it seems that original developer considered UI as major added-value and there were no official/documented way to decouple UI from other parts (there were documented way to add new devices).

There is also question of project dynamics, there are competing projects and this one probably did not attract much interest among outside developers. And of course software evolves, so this could be solved in the future.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required