Skip to content

Commit df3c1f0

Browse files
authored
close #177: handle unfinished arrivals (#178)
* initial implementation of #177 * wider verbose output * add documentation about unfinished arrivals * fix monitoring bug * add tests * update NEWS [ci skip]
1 parent 173ee82 commit df3c1f0

20 files changed

+256
-19
lines changed

NAMESPACE

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ S3method(get_server_count_selected,simmer)
4444
S3method(get_sources,simmer)
4545
S3method(get_sources,wrap)
4646
S3method(get_trajectory,simmer)
47+
S3method(handle_unfinished,trajectory)
4748
S3method(join,trajectory)
4849
S3method(leave,trajectory)
4950
S3method(length,trajectory)
@@ -116,6 +117,7 @@ export(get_server_count)
116117
export(get_server_count_selected)
117118
export(get_sources)
118119
export(get_trajectory)
120+
export(handle_unfinished)
119121
export(join)
120122
export(leave)
121123
export(log_)

NEWS.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# simmer 4.1.1
22

3+
## New features:
4+
5+
- New `handle_unfinished()` activity sets a drop-out trajectory for unfinished
6+
arrivals, i.e., those dropped from a resource (due to preemption, resource
7+
shrinkage or a rejected `seize`) or those that `leave` a trajectory (#178
8+
addressing #177).
9+
310
## Minor changes and fixes:
411

512
- Fix performance issues in data sources (#176).
613
- Update CITATION.
14+
- Fix monitored activity for preempted arrivals (as part of #178).
715

816
# simmer 4.1.0
917

R/RcppExports.R

+4
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ Leave__new <- function(prob) {
165165
.Call(`_simmer_Leave__new`, prob)
166166
}
167167

168+
HandleUnfinished__new <- function(trj) {
169+
.Call(`_simmer_HandleUnfinished__new`, trj)
170+
}
171+
168172
Leave__new_func <- function(prob) {
169173
.Call(`_simmer_Leave__new_func`, prob)
170174
}

R/simmer-methods.R

+5
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ peek.simmer <- function(.env, steps=1, verbose=FALSE) .env$peek(steps, verbose)
181181
#' preempted arrivals go to a dedicated queue, so that \code{queue_size} may be
182182
#' exceeded. If this option is \code{TRUE}, preempted arrivals go to the standard
183183
#' queue, and the maximum \code{queue_size} is guaranteed (rejection may occur).
184+
#' Whenever an arrival is rejected (due to a server drop or a queue drop), it
185+
#' will set the \code{finished} flag to \code{FALSE} in the output of
186+
#' \code{\link{get_mon_arrivals}}. Unfinished arrivals can be handled with a
187+
#' drop-out trajectory that can be set using the \code{\link{handle_unfinished}}
188+
#' activity.
184189
#'
185190
#' @return Returns the simulation environment.
186191
#' @seealso Convenience functions: \code{\link{schedule}}.

R/trajectory-activities.R

+36-5
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,20 @@
2424
#' @param .trj the trajectory object.
2525
#' @inheritParams select
2626
#' @param resource the name of the resource.
27-
#' @param amount the amount to seize/release, accepts either a numeric or a callable
28-
#' object (a function) which must return a numeric.
27+
#' @param amount the amount to seize/release, accepts either a numeric or a
28+
#' callable object (a function) which must return a numeric.
2929
#' @param continue a boolean (if \code{post.seize} OR \code{reject} is defined)
3030
#' or a pair of booleans (if \code{post.seize} AND \code{reject} are defined; if
3131
#' only one value is provided, it will be recycled) to indicate whether these
3232
#' subtrajectories should continue to the next activity in the main trajectory.
33-
#' @param post.seize an optional trajectory object which will be followed after a successful seize.
34-
#' @param reject an optional trajectory object which will be followed if the arrival is rejected.
33+
#' @param post.seize an optional trajectory object which will be followed after
34+
#' a successful seize.
35+
#' @param reject an optional trajectory object which will be followed if the
36+
#' arrival is rejected. Note that if the arrival is accepted (either in the
37+
#' queue or in the server) and then it is dropped afterwards due to preemption
38+
#' or resource shrinkage, then this trajectory won't be executed. Instead, see
39+
#' \code{\link{handle_unfinished}} for another, more general, method for
40+
#' handling all kinds of unfinished arrivals.
3541
#'
3642
#' @return Returns the trajectory object.
3743
#' @seealso \code{\link{select}}, \code{\link{set_capacity}}, \code{\link{set_queue_size}},
@@ -353,12 +359,37 @@ rollback.trajectory <- function(.trj, amount, times=Inf, check=NULL) .trj$rollba
353359
#' @param prob a probability or a function returning a probability.
354360
#'
355361
#' @return Returns the trajectory object.
362+
#'
363+
#' @details Arrivals that leave the trajectory will set the \code{finished} flag
364+
#' to \code{FALSE} in the output of \code{\link{get_mon_arrivals}}. Unfinished
365+
#' arrivals can be handled with a drop-out trajectory that can be set using the
366+
#' \code{\link{handle_unfinished}} activity.
367+
#'
368+
#' @seealso \code{\link{handle_unfinished}}, \code{\link{renege_in}}
356369
#' @export
357370
leave <- function(.trj, prob) UseMethod("leave")
358371

359372
#' @export
360373
leave.trajectory <- function(.trj, prob) .trj$leave(prob)
361374

375+
#' Handle Unfinished Arrivals
376+
#'
377+
#' Activity for setting a drop-out trajectory for unfinished arrivals, i.e.,
378+
#' those dropped from a resource (due to preemption, resource shrinkage or a
379+
#' rejected \code{\link{seize}}) or those that \code{\link{leave}} a trajectory.
380+
#'
381+
#' @inheritParams seize
382+
#' @param handler trajectory object to handle unfinished arrivals. A \code{NULL}
383+
#' value will unset the drop-out trajectory.
384+
#'
385+
#' @return Returns the trajectory object.
386+
#' @seealso \code{\link{leave}}, \code{\link{set_capacity}}
387+
#' @export
388+
handle_unfinished <- function(.trj, handler) UseMethod("handle_unfinished")
389+
390+
#' @export
391+
handle_unfinished.trajectory <- function(.trj, handler) .trj$handle_unfinished(handler)
392+
362393
#' Renege on some Condition
363394
#'
364395
#' Activities for setting or unsetting a timer or a signal after which the arrival will abandon.
@@ -379,7 +410,7 @@ renege_in.trajectory <- function(.trj, t, out=NULL) .trj$renege_in(t, out)
379410
#' (a function) which must return a string.
380411
#'
381412
#' @rdname renege_in
382-
#' @seealso \code{\link{send}}
413+
#' @seealso \code{\link{send}}, \code{\link{leave}}
383414
#' @export
384415
renege_if <- function(.trj, signal, out=NULL) UseMethod("renege_if")
385416

R/trajectory-class.R

+6
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ Trajectory <- R6Class("trajectory",
292292
)
293293
},
294294

295+
handle_unfinished = function(handler) {
296+
check_args(handler=c("trajectory", "NULL"))
297+
traj <- as.list(c(handler[]))
298+
private$add_activity(HandleUnfinished__new(traj))
299+
},
300+
295301
renege_in = function(t, out=NULL) {
296302
check_args(t=c("number", "function"), out=c("trajectory", "NULL"))
297303
traj <- as.list(c(out[]))

inst/include/simmer/activity/leave.h

+25
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,31 @@ namespace simmer {
4848
T prob;
4949
};
5050

51+
/**
52+
* Set a path to handle unfinished arrivals (from 'leave' or resources)
53+
*/
54+
class HandleUnfinished : public Fork {
55+
public:
56+
CLONEABLE(HandleUnfinished)
57+
58+
HandleUnfinished(const VEC<REnv>& trj)
59+
: Fork("HandleUnfinished", VEC<bool>(trj.size(), false), trj) {}
60+
61+
void print(unsigned int indent = 0, bool verbose = false, bool brief = false) {
62+
Activity::print(indent, verbose, brief);
63+
internal::print(brief, false);
64+
Fork::print(indent, verbose, brief);
65+
}
66+
67+
double run(Arrival* arrival) {
68+
Activity* next = NULL;
69+
if (heads.size())
70+
next = heads[0];
71+
arrival->set_dropout(next);
72+
return 0;
73+
}
74+
};
75+
5176
} // namespace simmer
5277

5378
#endif

inst/include/simmer/process/arrival.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ namespace simmer {
6565
Arrival(Simulator* sim, const std::string& name, int mon, Order order,
6666
Activity* first_activity, int priority = 0)
6767
: Process(sim, name, mon, priority), order(order), paused(false),
68-
clones(new int(0)), activity(first_activity), timer(NULL), batch(NULL)
69-
{ init(); }
68+
clones(new int(0)), activity(first_activity), timer(NULL),
69+
dropout(NULL), batch(NULL) { init(); }
7070

7171
Arrival(const Arrival& o)
7272
: Process(o), order(o.order), paused(o.paused), clones(o.clones),
73-
activity(NULL), attributes(o.attributes), timer(NULL), batch(NULL)
74-
{ init(); }
73+
activity(NULL), attributes(o.attributes), timer(NULL),
74+
dropout(NULL), batch(NULL) { init(); }
7575

7676
~Arrival() { reset(); }
7777

@@ -186,6 +186,8 @@ namespace simmer {
186186
batch = NULL;
187187
}
188188

189+
void set_dropout(Activity* next) { dropout = next; }
190+
189191
void set_renege(double timeout, Activity* next) {
190192
cancel_renege();
191193
timer = new Task(sim, "Renege-Timer",
@@ -222,6 +224,7 @@ namespace simmer {
222224
SelMap selected; /**< selected resource */
223225
Task* timer; /**< timer that triggers reneging */
224226
std::string signal; /**< signal that triggers reneging */
227+
Activity* dropout; /**< drop-out trajectory */
225228
Batched* batch; /**< batch that contains this arrival */
226229
ResMSet resources; /**< resources that contain this arrival */
227230

inst/include/simmer/process/arrival_impl.h

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
namespace simmer {
2727

2828
inline void Arrival::terminate(bool finished) {
29+
if (!finished && dropout) {
30+
activity = dropout;
31+
sim->schedule(0, this, priority);
32+
return;
33+
}
34+
2935
foreach_ (ResMSet::value_type& itr, resources) {
3036
Rcpp::warning("'%s': leaving without releasing '%s'", name, itr->name);
3137
itr->erase(this, true);

inst/include/simmer/resource/preemptive.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,22 @@ namespace simmer {
8888
typename T::iterator first = server.begin();
8989
if (first == server.end())
9090
return count;
91-
first->arrival->pause();
9291
if (sim->verbose) print(first->arrival->name, "PREEMPT");
9392
count += first->amount;
9493
server_count -= first->amount;
9594
server_map.erase(first->arrival);
9695
if (queue_size_strict) {
9796
if (!room_in_queue(first->amount, first->priority())) {
9897
if (sim->verbose) print(first->arrival->name, "REJECT");
98+
first->arrival->stop();
9999
first->arrival->unregister_entity(this);
100100
first->arrival->terminate(false);
101-
} else insert_in_queue(first->arrival, first->amount);
101+
} else {
102+
first->arrival->pause();
103+
insert_in_queue(first->arrival, first->amount);
104+
}
102105
} else {
106+
first->arrival->pause();
103107
preempted_map[first->arrival] = preempted.insert(*first);
104108
queue_count += first->amount;
105109
}

inst/include/simmer/simulator.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ namespace simmer {
141141
{
142142
Rcpp::Rcout <<
143143
FMT(10, right) << now_ << " |" <<
144-
FMT(12, right) << e_type + ": " << FMT(15, left) << e_name << "|" <<
145-
FMT(12, right) << a_type + ": " << FMT(15, left) << a_name << "| " << trail;
144+
FMT(12, right) << e_type + ": " << FMT(17, left) << e_name << "|" <<
145+
FMT(12, right) << a_type + ": " << FMT(17, left) << a_name << "| " << trail;
146146
if (flush) Rcpp::Rcout << std::endl;
147147
}
148148

man/add_resource.Rd

+6-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/handle_unfinished.Rd

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/leave.Rd

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/renege_in.Rd

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/seize.Rd

+10-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/RcppExports.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,17 @@ BEGIN_RCPP
526526
return rcpp_result_gen;
527527
END_RCPP
528528
}
529+
// HandleUnfinished__new
530+
SEXP HandleUnfinished__new(const std::vector<Environment>& trj);
531+
RcppExport SEXP _simmer_HandleUnfinished__new(SEXP trjSEXP) {
532+
BEGIN_RCPP
533+
Rcpp::RObject rcpp_result_gen;
534+
Rcpp::RNGScope rcpp_rngScope_gen;
535+
Rcpp::traits::input_parameter< const std::vector<Environment>& >::type trj(trjSEXP);
536+
rcpp_result_gen = Rcpp::wrap(HandleUnfinished__new(trj));
537+
return rcpp_result_gen;
538+
END_RCPP
539+
}
529540
// Leave__new_func
530541
SEXP Leave__new_func(const Function& prob);
531542
RcppExport SEXP _simmer_Leave__new_func(SEXP probSEXP) {
@@ -1335,6 +1346,7 @@ static const R_CallMethodDef CallEntries[] = {
13351346
{"_simmer_Rollback__new", (DL_FUNC) &_simmer_Rollback__new, 2},
13361347
{"_simmer_Rollback__new_func", (DL_FUNC) &_simmer_Rollback__new_func, 2},
13371348
{"_simmer_Leave__new", (DL_FUNC) &_simmer_Leave__new, 1},
1349+
{"_simmer_HandleUnfinished__new", (DL_FUNC) &_simmer_HandleUnfinished__new, 1},
13381350
{"_simmer_Leave__new_func", (DL_FUNC) &_simmer_Leave__new_func, 1},
13391351
{"_simmer_Clone__new", (DL_FUNC) &_simmer_Clone__new, 2},
13401352
{"_simmer_Clone__new_func", (DL_FUNC) &_simmer_Clone__new_func, 2},

src/activity.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ SEXP Leave__new(double prob) {
255255
return XPtr<Leave<double> >(new Leave<double>(prob));
256256
}
257257

258+
//[[Rcpp::export]]
259+
SEXP HandleUnfinished__new(const std::vector<Environment>& trj) {
260+
return XPtr<HandleUnfinished>(new HandleUnfinished(trj));
261+
}
262+
258263
//[[Rcpp::export]]
259264
SEXP Leave__new_func(const Function& prob) {
260265
return XPtr<Leave<Function> >(new Leave<Function>(prob));

0 commit comments

Comments
 (0)