IMHO, a separate precondition [1] cannot (ever) became a normal (sequential) assertion because it would be a race-condition. If such a timing constraint failure is to be implemented then an explicit timing condition should part of the routine contract.
If the precondition is the choice for such timing constraint (as we would infer it would, if the precondition recovers its sequential behavior), then which client should be responsible for the failure (thus, targeted with the proper launched exception)?
The one waiting? Or the "one[s]" that did not ensured a valid precondition? In a perfect world, the latter should be the choice, but that's impossible: we have no reference to it and, in fact, most likely it is not a client of the routine. As such, if such contract is to be ensured in the precondition, then the waiting client should be targeted, and a defensive programming approach should be taken in that client.
However, there are, IMHO, other (better) alternatives. For starters, the responsibility should lie in the routine, not on its client. Thus, it should not be part of its precondition.
For quite some time I've been thinking and working on an extension of DbC for timing constraints (an OO language approach for real-time programming was the initial motivation). (I have a MSc student that has proved the concept in a practical implementation, and, as we "speak", is working on the thesis writing).
Programs normally express algorithms from a pure logical perspective. Although some synchronization/order between some of the program elements is imposed, it does so regardless of the execution time involved (being attoseconds, or the age of the universe are, in this perspective, irrelevant).
However, timing constraints are not irrelevant in practical programs, and, of course, are critical in (hard) real-time programming (as are processor scheduling, memory handling, shared resources, priorities, and other system level requirements).
Time is always relative (even in classical Newton physics). We need a reference to make a time value meaningful. In a program, we could use an absolute external clock; the program execution time; or, the routine execution time. Clearly, the latter one, is the best choice for expression timing constraints.
So, if a language abstracts and implements a special integer query -- elapsed_time (Duration?)-- representing the current routines execution time (starting at its invocation, not its body execution) then predicates can be formed to express deadline requirements or timeouts for handling a request.
However, as suggested before, preconditions are not the proper assertion to place such a contract. Postconditions would be the immediate alternative. But what is the meaning of a postcondition before the routine executes its body? (I remember some discussion on this matter 20 years ago at CORDIE'06 at York, UK.)
Preconditions, postconditions and class invariants are discrete assertions, they only apply in discrete times in the program execution (before, after routine execution, and at stable times in the object lifetime). For practical useful applications, timing constraints should be applied continuously (or else, for example a missed deadline would be detected to late, if ever, for a proper failure handling).
Thus we propose a new kind of contract: routines invariants. Unlike class invariants, a routine invariant (as its name strongly suggests) is a continuous predicate applicable across routine's execution. Adapting an example from [https://www.eiffel.org/doc/solutions/Producer-consumer]:
store (a_buffer:
separate BOUNDED_BUFFER [INTEGER]; an_element: INTEGER; timeout:
INTEGER)
-- Store `an_element' into `a_buffer'.
invariant
elapsed_time < timeout
require
not a_buffer.is_full
do
a_buffer.put (an_element)
ensure
not a_buffer.is_empty
a_buffer.count = old a_buffer.count + 1
end
Here, as it should, a timeout is the responsibility of the routine, not the client, and there is no hidden contract.
Routine's invariants could also be very useful to express frame conditions (what never changes), and other logical predicates (for instance, ensure that the temperature of a boiled is always within a given interval).
Perhaps a (much stronger) continuous variation of class invariants could also be considered (across invariant ?).
Best regards,
-miguel
[1] I have a slightly different view of this type of assertions. I named then concurrent assertions, applicable to any type of assertion: preconditions, postconditions, checks, and invariants. A concurrent condition -- which within an assertion, becomes a concurrent assertion -- is a predicate whose value may depend on a different processor (active entity) than the one testing the predicate [PhD thesis, 2007, chapter 5, section 1.2, https://sweet.ua.pt/mos/pubs/phd-thesis_en.pdf]. (Please note that this document is a draft translation of my thesis -- at the time, written in Portuguese -- so translation errors may definitely exist.)
In my MP-Eiffel concurrent proposal, concurrent conditions not only express concurrent assertions (with the safe behavior of, when necessary, being a conditional synchronization application), but also express external synchronization, when used in conditional instructions (as with concurrent assertions, any other behavior would be a race-condition).
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/ef508895-69aa-41e1-b1a6-a6687ce2ef27%40gobosoft.com.
-- Miguel Oliveira e Silva IEETA-DETI, University of Aveiro, Portugal