Ada: the select/else statement on protected entries. What does it do? - ada

I have a doubt about Ada, and in particular about the select statement when used in conjunction with a protected entry. Let's consider the following code fragment:
select
Protected_Object.Some_Entry;
else
DoSomethingElse;
end select;
My question is simple: when the select statement is reached, what happens? In particular, what I want to know is: does the else branch get chosen only if Some_Entry's guard is closed or does it get chosen even if the guard is opened but the entry is "occupied" (ie: there's already a call to Some_Entry executing) and thus cannot be called immediately??

I believe that the else branch is chosen only if the entry's guard is closed (i.e. the barrier condition is false). The sequence of events for a protected entry call is given by RM 9.5.3(8):
A new protected action is started on the object.
The named entry is checked to see if it is open (i.e. the barrier condition is true); if open, the entry call is said to be selected immediately, and then the entry body is executed.
Starting a protected action may involve a short delay if another task is performing a protected action on the same object (9.5.1(4)). However, the intent is that this delay is always very short. If a protected subprogram or entry does anything that could block the program, this is considered an error (9.5.1(8-18)). Thus, waiting until another task releases a protected object is supposed to be a very short wait if at all; on a multi-processor system, it's perfectly acceptable to implement this wait by spinning (essentially while Protected_Object_Is_In_Use(Obj) loop null; end loop;) as opposed to waiting on a queue.
Thus, my reading of 9.5.3(8) is that the definition of "selected immediately" does not take into account the short wait needed if another task is engaged in a protected action on the object. If the task has to wait, it does so. If, once it is able to grab the object, it finds that the barrier is true, then the entry is "selected immediately". This may not quite fit our idea of what the English word "immediately" means, but it's how the term is defined.
Thus, for a conditional entry call, RM 9.7.3 says that the entry call is cancelled (and the else branch executed) if it is not selected immediately. Using the definition in 9.5.3(8), this means that the else branch is executed only if the barrier condition is false (after the task succeeds in grabbing the protected object).

The 'else' branch is chosen if Some_Entry is not immediately accessible (either because there is already another task accessing the protected object, or because the guard prevents calling the entry in the first place.
The goal of the 'else' branch is basically that your task does not stay blocked and thus might miss a timeout or an action to execute on a regular schedule. So when you use 'else', the select statement cannot be blocking (unless of course the entry itself, once called, takes forever).
Edit: As demonstrated below by #simonwright, this answer is incorrect: the 'else' branch is taken when the guard is False, otherwise the task will block on the protected object's entry. By design, such entries should perform their work in a very limited amount of time.

Related

How to gracefully shut down reactive kafka-consumer and commit last processed record?

My painful hunt for this feature is fully described in disgustingly log question: Several last offsets aren't getting commited with reactive kafka and it shows my multiple attemps with different failures.
How would one subscribe to ReactiveKafkaConsumerTemplate<String, String>, which will process the records in synchronous way (for simplicity), and will ack/commit every 2s AND upon manual cancellation of stream? Ie. it works, ack/commits every 2s. Then via rest/jmx/whatever comes signal, the stream terminates and ack/commits the last processed kafka record.
After a lot of attempts I was able to come up with following solution. It seems to work, but it's kinda ugly, because it's very "white-box" where outer flow highly depends on stuff happening inside of other methods. Please criticise and suggest improvements. Thanks.
kafkaReceiver.receive()
.flatMapSequential(receivedKafkaRecord -> processKafkaRecord(receivedKafkaRecord), 16)
.takeWhile(e-> !stopped)
.sample(configuration.getKafkaConfiguration().getCommitInterval())
.concatMap(offset -> {
log.debug("ack/commit offset {}", offset.offset());
offset.acknowledge();
return offset.commit();
})
.doOnTerminate(()-> log.info("stopped."));
What didn't work:
A) you cannot use Disposable.dispose, since that would break the stream and your latest processed record won't be committed.
B) you cannot put take on top of stream, as that would cancel the stream and you won't be able to commit either.
C) not sure how I'd be able to intercorporate usage of errors here.
Because of what didn't work stream termination is triggered by boolean field named stopped, which can be set anyhow.
Flow explained:
flatMapSequential — because of inner parallelism and necessity to commit N only if all N-1 was processed.
processKafkaRecord returns Mono<ReceiverOffset>, ie. the offset of processed record to have something to ack/commit. When stopped the method will skip processing and return Mono.empty
take will stop stream if stopped, this has to be put here becaue of possibility of whole sample interval consisting only from "empties"
rest is simple: sample by given interval, commit in order. If sample does return empty record, commit is skipped. Finally we log that stream is cancelled.
If anyone know how to improve, please criticise.

Progress-4gl: How does transaction scope apply to external program calling?

I need some help understanding transaction scoping for procedures/programs outside the current program.
Suppose I've three program, program A, program B and program C. Inside program A, I've a procedure that has some lines in it wrapped inside a do transaction (not strongly typed) block. Within that do transaction block, it calls another Program B. Upon return from program B there is an undo, leave command. Within the same transaction block, it calls program C and has an undo, leave after this call too.
My question is, if within the transaction block, program B executes without errors, but program c returned an error, will the undo,leave after program C call will also undo transactions that happened inside program B?
Procedure do_something:
some processing....
do transaction:
error-message = "".
{run programB.p}
if error-message <> "" then undo, leave.
some further processing...
error-message = "".
{run programC.p}
if error-message <> "" then undo, leave.
end. /* end of do transaction */
end procedure.
Yes. In the example that you describe everything gets rolled back.
It is not so much that it is "extended" per se but just that the transaction includes everything that happens in that session from the point in time when it is enabled all the way until it is either committed or rolled back. Internal procedures, external procedures, user defined functions, methods of classes, trigger code etc.
"In that session" is important - if you call a procedure on an app server that activity is NOT included since it is its own process with its own distinct transaction context.
When app servers are involved things get messy. The original caller has no (built-in) capability to know what to roll back in the called app server session. The app server call could return an error that causes the caller to roll back if it encounters problems but the caller could also decide to trap and ignore that error.
Yes. Everything happening in the transaction block will be undone.

Creating many batches (SysOperation Framework) very quickly doing similar processes - "Cannot edit a record in LastValue (SysLastValue)"?

I have a SysOperation Framework process that creates a ReliableAsynchronous batch to post packing slips and several get created at a time.
Depending on how quickly I click to create them, I get:
Cannot edit a record in LastValue (SysLastValue).
An update conflict occurred due to another user process deleting the record or changing one or more fields in the record.
And
Cannot create a record in LastValue (SysLastValue). User ID: t edit a, Class.
The record already exists.
On a couple of them in the BatchHistory. I have this.parmLoadFromSysLastValue(false); set. I'm not sure how to prevent writing to SysLastValue table.
Any idea what could be going on?
I get this exception a lot too, so I've created the habit of catching DuplicateKeyException in my service operation. When it is thrown, catch it and retry (for a default of 5x).
The error occurs when a lot of processes run simultaneously, like you are doing now.
DupplicateKeyException can be caught inside a transaction so you could improve by putting a try/catch around the code that does the insert in the SysLastValue table if you can find the code.
As far as I can see these are the only to occurrences where a record is inserted in this table (except maybe in kernel):
InventUnusedDimCleanUp.serialize()
SysAutoSemaphore.autoSemaphore()
Put a breakpoint there and see if that code is executed. If so you can add a try/catch with retry and see if that "fixes" it.
You could also use the tracing cockpit and the trace parser to figure out where that record is inserted if it's not one of those two.
My theory about LoadFromSysLastValue: I believe setting this.parmLoadFromSysLastValue(false) does not work since it is only taken into account when the dialog is started, not when your operation is executed. When in batch, no SysLastValue will be used to initialize your data contract as you want it to use the exact parameters you have supplied in your data contract .
It's because of the code calling SysOperationController.savelast() while in batch, my solution is to set loadFromSysLastValue to false in SysOperationController.loadFromSysLastValue() as part of the in batch check:
if (!this.isInBatch())
{
.....
}
//Begin
else
{
loadFromSysLastValue = false;
}
//End

Why does execve() does not return on success?

I have read the man pages.
All I understood from this link http://support.sas.com/documentation/onlinedoc/sasc/doc750/html/lr2/zid-7281.htm is that
A successful call to execve does not have a return value because the new process image overlays the calling process image
I am not very clear as to why this will happen ? And if the new process overlays the calling process, why does it return on failure only and not on success ?
Because if it fails to do what it is trying to do, i.e. replace the process with the new one, then it makes sense to return, to inform the caller that it failed.
If it succeeds, then the code that called execve() is no longer present, it has been replaced by the successful execution of that function, so obviously it cannot do anything any more. Returning is such a thing.

Is there a way to acquire a lock in Redis? (Node.js)

My Node.js application accepts connections from the outside. Each connection handler reads a SET on Redis, eventually modifies the set itself, then moves on. The problem is that in the meanwhile another async connection can try to read the same SET and try to update it or decide its next step based on what it reads.
I know that Redis does its best to be atomic, but this is not quite sufficient for my use case. Think about this: the set is read to understand if it's FULL (there is a business rule for that). If it's FULL, then something happens. The problem is that if there is one only slot left, two semi-concurrent connections could think each one is the last one. And I get an overflow.
I there a way to keep a connection "waiting" for the very short time the other eventually needs to update the set state?
I think this is a corner case, very very unluckely... but you know :)
Using another key as the "lock" is an option, or does it stink?
How about using blpop to do locking. blpop key 5 to wait 5 seconds for key. At start put item(to identify queue is not empty) at key. The connection acquiring the lock should remove item from key. The next connect then can't acquire the lock, because empty, but blpop has the following nice property:
Multiple clients can block for the same key. They are put into a
queue, so the first to be served will be the one that started to wait
earlier, in a first-BLPOP first-served fashion.
When connection which acquired lock has finished task it should put back item back in queue, then the next connection waiting can acquire lock(item).
You may be looking for WATCH with MULTI/EXEC. Here's the pattern that both threads follow:
WATCH sentinel_key
GET value_of_interest
if (value_of_interest = FULL)
MULTI
SET sentinel_key = foo
EXEC
if (EXEC returned 1, i.e. succeeded)
do_something();
else
do_nothing();
else
UNWATCH
The way this works is that all of the commands between MULTI and EXEC are queued up but not actually executed until EXEC is called. When EXEC is called, before actually executing the queued instructions it checks to see if sentinel_key has changed at all since the WATCH was set; if it has, it returns (nil) and the queued commands are discarded. Otherwise the commands are executed atomically as a block, and it returns the number of commands executed (1 in this case), letting you know you won the race and do_something() can be called.
It's conceptually similar to the fork()/exec() Unix system calls - the return value from fork() tells you which process you are (parent or child). In this case it tells you whether you won the race or not.

Resources