maximum concurrent tasks, and thread safe task list
optionally sort tasks_at_time results by task frequency
add maximum concurrent tasks
move executing task decrementing / incrementing to scheduler
handling a slow task needs to have the time of the still-executing ta…
…sk passed in
make adding / removing tasks from a scheduler all mutexy so you can a…
…dd or remove tasks as part of a task
Merge branch 'master' into maximum-concurrency
synchronize returns the last evaluated statement just like methods in ruby, so there's no need to do all this work with locals:
scheduler.executing_tasks += 1
There are other places in the branch where this is the case.
Organization-wise, it might be better to make a TaskCollection or something that's thread-safe that handles the deleting, adding, finding the tasks at a specific time, etc internally and you could more or less make it behave similar to an array. There's a lot of work here that doesn't necessarily feel like the Scheduler's responsibility.
Does the mutex for adding tasks and the mutex for incrementing/decrementing the number of executing tasks actually need to be shared?
It doesn't need to be shared, no. I'm basically just using mutexes as "things that give me access to a synchronize block". Would there be a benefit to using separate mutexes?
There is a benefit to making your locking as fine-grained as possible. Only one synchronize block for a given mutex can be executing simultaneously. This means that if any other thread tries to call synchronize with the same mutex, it will have to wait until the first synchronize block is done executing. In recurrent, a counter increment/decrement might wait on anything that manipulates tasks, or vice versa, since they share a mutex. Using the finest-grained mutexes that you are able to will reduce this sort of contention and keep things flowing as smoothly as possible. Given the nature of the tasks you're using the mutex for, I doubt it will cause any serious contention as is, but who knows in the future.
s/can be executing simultaneously/can be executing at any given time/
What happens if this task is removed from the task list while it's waiting to be executed, or while the task is still running? Right now it seems like it will continue to run.
This doesn't guarantee the order in which waiting tasks will become one of the executing tasks. Is the order important?
I think it's okay if it continues to run. Like with dashboard configs, there's going to be some expectation of delay of respecting changes to tasks on the fly.
The goal is to be running the tasks with smaller frequencies, e.g. tasks that run once a minute, before tasks that run hourly / daily etc. I think there's something in Scheduler#tasks_at_time about that.
You have a race condition here. The value of @executing_tasks could change between the end of the mutex.synchronize block and the subsequent retrieval of it on this line. As I noted in the comment earlier, the synchronize block should return the last evaluated statement, which should be the value of @executing_tasks after @executing_tasks += 1 executes.
@executing_tasks += 1
sort tasks_at_time by frequency if max concurrency is set
refactor tasks into TaskCollection
put the sort_by_frequency stuff in the right place
pass &block to method_missing
ensure we're executing tasks in order of frequency when there is a co…
move some scheduler tests to task_collection
delete empty spec
fix wrong method name
fix wrong method
more refactor fallout