On Sun, Apr 2, 2017 at 8:10 AM, Ben Coman <btc@openinworld.com> wrote:
>
>
>
> On Sun, Apr 2, 2017 at 3:11 AM, Petr Fischer <petr.fischer@me.com> wrote:
>>
>>
>> > On Thu, 30 Mar 2017, Eliot Miranda wrote:
>> >
>> > > Once the active process is in a tight loop the delay is effectively
>> > disabled because the tight loop effectively shuts out the heartbeat thread
>> > and hence the system never notices that the delay has expired.
>> >
>> > I think that won't happen, because the process scheduler (O(1), CFS, BFS) on
>> > linux is not cooperative. So, the kernel will periodically preempt the main
>> > thread and run the heartbeat thread no matter what their priorities are. The
>> > higher priority only provides lower jitter on the heartbeat thread.
>> >
>> > Levente
>>
>> Is there some test case or code, that I can run in Pharo and evaluate if kernel sheduler is working correctly (with heartbeat thread at normal priority).
>> I need to test it under FreeBSD.
>>
>> Thanks! pf
>
>
> Just for starters, what result do you get for my multi-priority fibonacci stress test... 
> http://forum.world.st/Unix-heartbeat-thread-vs-itimer-tp4928943p4938456.html 
>
> cheers -ben
>

I got curious to read up on the FreeBSD scheduler.

FreeBSD has the same constraint as Linux such that "Only the super-user may lower priorities."  
https://www.freebsd.org/cgi/man.cgi?query=setpriority&sektion=2


From https://classes.cs.uoregon.edu/13F/cis607distcomp/PPT/FreeBSDscheduler(McKay).pdf
Each CPU has (a KSeq) three arrays of run queue indexed by priority
* The Current queue receives interactive, real time and interrupt threads
* The Next queue receives everything else except idle threads
* When Current queue is empty, the two queues swap.
* The third queue hold idle threads, and is only used when there are no other runnable threads


From http://web.cs.ucdavis.edu/~roper/ecs150/ULE.pdf
ULE: A Modern Scheduler For FreeBSD

A thread is assigned to a queue until it sleeps, or for the duration of a slice. 
The base priority, slice size, and interactivity score are recalculated each time a slice expires. 
The thread is assigned to the Current queue if it is interactive or to the Next queue otherwise. 
Inserting interactive tasks onto the Current queue and giving them a higher priority 
results in a very low latency response.

In ULE the interactivity of a thread is determined using its voluntary sleep time and run time. 
The voluntary sleep time is recorded by counting the number of ticks that have passed between 
a sleep() and wakeup() or while sleeping on a condition variable. 
The run time is simply the number of ticks while the thread is running.  
The scheduler uses the interactivity score to determine whether or not a thread 
should be assigned to the Current queue when it becomes runnable. 

On x86, FreeBSD has a default HZ of 100, 
and a minimum slice value of 10ms and maximum slice value of 140ms. 
Interactive tasks receive the minimum slice value. 
This allows us to more quickly discover that an interactive task is no longer interactive. 


From http://ptgmedia.pearsoncmg.com/images/9780321968975/samplepages/9780321968975.pdf
The Design and Implementation of the FreeBSD Operating System

The scheduling policy initially assigns a high execution priority to each thread 
and allows that thread to execute for a fixed time slice. 
Threads that execute for the duration of their slice have their priority lowered, 
whereas threads that give up the CPU (usually because they do I/O) are allowed to remain at their priority. 
Threads that are inactive have their priority raised. 

Some tasks, such as the compilation of a large application, may be done in many 
small steps in which each component is compiled in a separate process. 
No individual step runs long enough to have its priority degraded, 
so the compilation as a whole impacts the interactive programs. 
To detect and avoid this problem, the scheduling priority of a child 
process is propagated back to its parent. When a new child process is started, 
it begins running with its parent’s current priority. 
As the program that coordinates the compilation (typically make) starts many compilation steps, 
its priority is dropped because of the CPU-intensive behavior of its children. 
Later compilation steps started by make begin running and stay at a lower priority, 
which allows higher-priority interactive programs to run in preference to them as desired.

Resuming a thread ...   
If any threads are placed on the run queue and one of them has a scheduling 
priority higher than that of the currently executing thread, 
it will request that the CPU be rescheduled as soon as possible.
Real-time and interrupt threads do preempt lower-priority threads. 
The kernel can be configured to preempt timeshare threads executing 
in the kernel with other higher-priority timeshare threads. 
This option is not used by default as the increase in context switches 
adds overhead and does not help make timeshare threads response time more predictable


From https://github.com/freebsd/freebsd/blame/master/sys/kern/sched_ule.c
and substituting defined constants...
PRIO_MIN               -20
PRIO_MAX                20
SCHED_INTERACT_THRESH   30
SCHED_INTERACT_HALF     50 = (SCHED_INTERACT_MAX / 2)
SCHED_INTERACT_MAX     100
PRI_MIN_TIMESHARE   120
PRI_MAX_TIMESHARE   223 = (PRI_MIN_IDLE - 1)
PRI_MIN_IDLE            224

SCHED_PRI_NRESV           40 = (PRIO_MAX - PRIO_MIN)
PRI_TIMESHARE_RANGE 104 = (PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE + 1)
PRI_INTERACT_RANGE    32 = ((PRI_TIMESHARE_RANGE - SCHED_PRI_NRESV) / 2)

PRI_MIN_INTERACT   120 = (PRI_MIN_TIMESHARE)
PRI_MAX_INTERACT   153 = (120 + PRI_INTERACT_RANGE - 1)

PRI_MIN_BATCH   152 = (PRI_MIN_TIMESHARE + PRI_INTERACT_RANGE)
PRI_MAX_BATCH   223 = (PRI_MAX_TIMESHARE)
SCHED_PRI_NHALF  20 = (SCHED_PRI_NRESV / 2)
SCHED_PRI_MIN   172 = (PRI_MIN_BATCH + SCHED_PRI_NHALF)
SCHED_PRI_MAX   203 = (PRI_MAX_BATCH - SCHED_PRI_NHALF)
SCHED_PRI_RANGE            30 = (SCHED_PRI_MAX - SCHED_PRI_MIN + 1)

sched_interact                  30 = (SCHED_INTERACT_THRESH)


sched_interact_score() 
  if (sleep/run)>1,  interact_score = 50 / (sleep/run)    
  if (sleep/run)=1,  interact_score = 50 
  if (sleep/run)<1,  interact_score = 50 * (2 - (sleep/run)) 


sched_priority()
  * If the score is interactive we place the thread in the realtime
  * queue with a priority that is less than kernel and interrupt
  * priorities.  These threads are not subject to nice restrictions.
  *
  * Scores greater than this are placed on the normal timeshare queue
  * where the priority is partially decided by the most recent cpu
  * utilization and the rest is decided by nice value.
  *
  * The nice value of the process has a linear effect on the calculated
  * score.  Negative nice values make it easier for a thread to be
  * considered interactive. Default nice is 0.
  *
  score = sched_interact_score() + nice); 
  if (score < (30))
     priority = 120 + score*34/30    // = 120 + (153 - 120 + 1) / 30 * score 
  else
     priority = 201 + nice           // = 172 + 30 - 1 + nice


sched_add(struct thread *td, int flags)
  * Select the target thread queue and add a thread to it.  
  * Request preemption or IPI a remote processor if required.
  * Recalculate the priority before we select the target cpu or run-queue.
  *
  if (PRI_BASE(td->td_pri_class) == PRI_TIMESHARE)
     sched_priority(td);
  ...

So it seems as long as "sleep/run > 2" then it seems 
FreeBSD heatbeat-thread will get an interactive priority bump.

cheers -ben