odem-rs-core 0.1.0

Core components of the ODEM-rs simulation framework
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
//! This module provides the `Agent` structure, which represents an isolated
//! thread of execution with its own copy of externally visible shared data.
//!
//! # Overview
//!
//! Each agent can be thought of as the root of a tree made up of [jobs](Job),
//! each having its unique copy of shared data accessible from the outside.
//!
//! The lifecycle of agents prevents leakage of internal state into the
//! surrounding lexical context and prevents the surrounding context from
//! influencing the agent's execution via side effects.
//!
//! Agent isolation is achieved through the `Actions` trait, which
//! parameterizes the `Agent` structure. The `Actions` trait allows users to
//! define agents without explicitly naming the asynchronous lifecycle,
//! providing fine-grained control over the interface visibility.
//!
//! ## Key Components
//!
//!  - **Agent:** The [`Agent`] structure represents an isolated thread of
//!    execution with its own shared data and lifecycle. It automatically
//!    dereferences to the shared data it has been initialized with, and mutable
//!    references to the shared data may only be obtained before an agent is
//!    activated, which is enforced at compile-time.
//!
//!  - **Actions:** The [`Actions`] trait is used as the single parameter to the
//!    `Agent` structure, aiding in the specification of user-defined
//!    behavior. It allows users to specify the simulation configuration, the
//!    type of shared data accepted, the resulting type of futures, and the
//!    [`Settle`] action taken once the agent terminates normally.
//!
//!  - **Lifecycle:** The [`Lifecycle`] trait allows implementors to be used
//!    like a function that can be called once with a shared reference to some
//!    data and a simulation context, returning a `Future` that can reference
//!    these arguments. It is used in the `Actions` trait in an all-quantified
//!    (HRTB) form over the lifetime of its arguments and return value,
//!    enabling compiler-enforced agent isolation.
//!
//!  - **Behavior:** The [`Behavior`] trait provides a user-friendly way to
//!    endow an agent with a lifecycle. Implementing this trait for a type
//!    allows it to be used to create agents. This trait is blanket implemented
//!    for pairs of type instances and `Lifecycle`s that can be used if
//!    implementing `Behavior` is not easy due to orphan rules or multiple
//!    lifecycles should be supported by the same type.
//! 
//!  - **Builder:** A builder type aiding in the configuration of agents with
//!    detailed specifications, such as a unique name, lifecycle, rank, and
//!    source code location.
//!
//! ## Examples
//!
//! Simple agent with a lifecycle and no return value:
//!
//! ```
//! # use odem_rs_core::{agent::{Actions, Agent, Behavior}, simulator::Sim, config::DefaultConfig};
//! # use core::pin::pin;
//! struct Foo(usize);
//!
//! impl Behavior for Foo {
//!     type Output = ();
//!
//!     async fn actions(&self, sim: &Sim) {
//!         // implement agent behavior
//!     }
//! }
//!
//! async fn sim_main(sim: &Sim) {
//!     let foo = pin!(Agent::new(Foo(3)));
//!     let h = sim.activate(foo);
//!     sim.advance(10.0).await;
//! }
//! ```
//!
//! Simple agent with lifecycle returning a value:
//! ```
//! # use odem_rs_core::{ops::defer, agent::{Actions, Agent, Behavior}, simulator::Sim, config::DefaultConfig, Puck};
//! # use core::pin::pin;
//! struct Foo(usize);
//!
//! impl Behavior for Foo {
//!     type Output = f32;
//! 
//!     async fn actions(&self, sim: &Sim) -> f32 {
//!         self.0 as f32
//!     }
//! }
//!
//! async fn sim_main(sim: &Sim) {
//!     let foo = pin!(Agent::new(Foo(3)));
//!     let x: f32 = sim.activate(foo).await;
//! }
//! ```
//!
//! Multiple agents with varying lifecycles:
//! ```
//! # use odem_rs_core::{ops::defer, agent::{Actions, Agent, Behavior}, simulator::Sim, config::DefaultConfig, Puck};
//! # use core::pin::pin;
//! use std::rc::Rc;
//! struct Foo;
//!
//! impl Foo {
//!     async fn one(&self, sim: &Sim) -> f64 {
//!         sim.advance(1.0).await;
//!         5.0
//!     }
//!
//!     async fn two(self: &Rc<Self>, sim: &Sim) {
//!         // these activities require a `&Rc<Self>` instead of the usual `&Self`
//!         sim.advance(2.0).await;
//!     }
//! }
//!
//! async fn sim_main(sim: &Sim) {
//!     let agent_smith = pin!(Agent::new((Foo, Foo::one)));
//!     let agent_brown = pin!(Agent::new((Rc::new(Foo), Foo::two)));
//!     let x: f64 = sim.activate(agent_smith).await;
//!     sim.activate(agent_brown).await;
//! }
//! ```

use core::{
	any::{Any, type_name},
	future::{Future, IntoFuture, Pending, pending},
	marker::PhantomData,
	ops::{Deref, DerefMut},
	panic::Location,
	pin::Pin,
	task::{Context, Poll},
};

use crate::{
	Active, Dispatch, ExitStatus,
	config::{Config, DefaultConfig},
	continuation::{Continuation, Label, Share, erased::State as ContState},
	error,
	job::{Builder as JobBuilder, Checked, Job, Puck as JobPuck, Settle, Unchecked},
	ptr::{AsIrc, Irc, Lease, LeasedMut},
	simulator::{Prec, Sim},
};

/* *************************************************** Internal Actions-Trait */

/// Endows an active object with behavior for a specific (or generic)
/// [configuration](Config).
///
/// This is the preferred way to provide a behavioral description to agents,
/// since it prevents having to name un-nameable types.
/// 
/// The base name of the agent's [`Label`] can optionally be set. Otherwise it
/// will default to the type name.
///
/// # Examples
///
/// ```
/// # use odem_rs_core::{agent::{Agent, Behavior}, simulator::Sim};
/// # use core::pin::pin;
/// # async fn sim_main(sim: &Sim) {
/// struct MrSmith(f64);
///
/// impl Behavior for MrSmith {
///     type Output = ();
///
///     async fn actions(&self, sim: &Sim) {
///         sim.advance(self.0).await;
///     }
/// }
///
/// // later
/// let agent = pin!(Agent::new(MrSmith(5.0)));
/// sim.activate(agent);
/// # }
/// ```
pub trait Behavior<C: ?Sized + Config = DefaultConfig> {
	/// The result type of the `actions` method.
	type Output;

	/// Async method receiving a shared reference to the active object and one
	/// to the [simulation context](Sim).
	///
	/// Since our simulators don't support parallel execution, we don't require
	/// `Send` for the future returned by `actions`.
	#[allow(async_fn_in_trait)]
	async fn actions(&self, sim: &Sim<C>) -> Self::Output;

	/// Method determining the base name of the type implementing this trait.
	///
	/// Defaults to the type name but may be overridden.
	fn name(&self) -> &'static str {
		type_name::<Self>()
	}
}

/// This trait is used as the single parameter to the [`Agent`] type and
/// is intended to be helpful for the construction of user-defined agents.
///
/// The main problem that is solved by the trait concerns the fact that the
/// asynchronous [`Lifecycle`] of an `Agent` cannot be named since its
/// definition is generated by the compiler. By generating an `Agent` whose only
/// type-argument `impl`s the `Actions`-trait, a user can get around naming
/// the lifecycle-part of the agent.
///
/// Note that it is possible to underspecify the result type, e.g. by omitting
/// information about the associated [`Subject`](Actions::Subject). This
/// effectively hides those parts of the interface that rely on this
/// information, giving a very fine-grained control about what is possible to
/// do by the caller and what isn't.
///
/// # Examples
///
/// Directly returning an `Agent` instead of just the initialized subject:
/// ```
/// # use odem_rs_core::{agent::{Agent, Actions}, config::DefaultConfig, ptr::Lease, simulator::Sim};
/// # use core::pin::pin;
/// # async fn sim_main(sim: &Sim) {
/// struct Foo;
///
/// impl Foo {
///     fn process<'p>() -> Lease<'p, Agent<impl Actions<
///         Config = DefaultConfig,
///         Subject = Self, // allowed to hide either of those types;
///         Output = (),    // caller can't rely on their existence
///     > + use</* nothing is borrowed */>>> {
///         Agent::build()
///             .with_subject(Foo)
///             .with_actions(Foo::actions)
///             .finish()
///     }
///
///     async fn actions(&self, sim: &Sim) {
///         // implement the behavior
///     }
/// }
///
/// // later
/// let my_foo = pin!(Foo::process());
/// sim.activate(my_foo);
/// # }
/// ```
///
/// Yeah, this is not overly readable. Do it only if you have to.
pub trait Actions {
	/// Specifies the [simulation configuration](Config) used by the
	/// [Action](Self::Action) type.
	type Config: Config;
	/// Specifies the type of shared data accepted by the [`Action`] type.
	///
	/// [`Action`]: Self::Action
	type Subject: Any;
	/// Specifies the resulting type of all the potential futures produced
	/// by the [`Action`](Self::Action) type.
	type Output;
	/// Specifies the [`Settle`]-action taken once the agent terminates
	/// normally.
	type Settle: Settle<Self::Output>;

	/// Specifies a set of methods all-quantified over lifetime that can be used
	/// to derive a [`Future`] given a reference to [shared] data and a
	/// [simulation context](Sim).
	///
	/// This construction is used to enforce agent-isolation statically
	/// because it prevents the produced future from explicitly referencing
	/// non-static context outside the provided simulation context and the
	/// reference to the shared data. Consequently, this reduces the
	/// surface-area of side effects regarding the inner agent state to
	/// the well-defined interface provided by these two references, analogous
	/// to the way operating systems are used to orchestrate interactions
	/// between system agents through a well-defined interface.
	///
	/// Crucially, it is enough to demand the HRTB without actually having to
	/// run it for different lifetimes in order to trigger the corresponding
	/// checks in the compiler.
	///
	/// See the [`Lifecycle`]-trait for more information.
	///
	/// [shared]: Self::Subject
	type Action: for<'p> Lifecycle<'p, Self::Config, Self::Subject, Output = Self::Output>;
}

/// This trait allows its implementors to be used like a function that can be
/// called once with a shared reference to some data and a
/// [simulation context](Sim) and return a [`Future`] that can reference
/// these arguments.
///
/// It is used in the [`Actions`]-trait in an all-quantified ([HRTB]) form over
/// the lifetime of its arguments and return value, enabling compiler-enforced
/// [`Agent`]-isolation.
/// 
/// [HRTB]: https://doc.rust-lang.org/nomicon/hrtb.html
pub trait Lifecycle<'p, C: Config, I: Any + ?Sized> {
	/// Specifies the [`Output`](Future::Output)-type of the [`Future`].
	type Output;
	/// The resulting [`Future`] of the [init](Self::init)-method.
	type Future: Future<Output = Self::Output> + 'p;

	/// Trait-method that uses the implementor-instance to construct the
	/// [`Future`](Self::Future) given the shared reference to some data and
	/// the [simulation context](Sim).
	fn init(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future;
}

/* ****************************************************************** Agent */

/// Isolated thread of execution that has its own copy of externally visible
/// shared data.
///
/// Each [`Agent`] can be thought of the root of a tree made up of [jobs](Job)
/// that has its own unique copy of externally visible data that is shared
/// amongst the jobs in the tree.
///
/// The [`Lifecycle`] of agents prevents leakage of internal state into the
/// surrounding (lexical) context as well as preventing the surrounding context
/// from influencing the execution of the agent via side effects. This is in
/// contrast to the way jobs allow the free sharing of all lexically visible
/// state between each other and their creators.
///
/// Agent isolation is achieved through the [`Actions`]-trait parameterizing
/// the structure. More concretely, the lifecycle of an `Agent` is prevented
/// from sharing data with its surrounding context by asking for a higher-ranked
/// trait-bound on the lifetime of arguments and return types, preventing
/// non-`'static` data, i.e. locally visible identifiers, from being referenced
/// by the closure.
///
/// Note that agents automatically dereference to the shared data they have
/// been initialized with as a matter of convenience. Mutable references to the
/// shared data may only be obtained before an `Agent` is [activated] which is
/// enforced at compile-time.
///
/// # Examples
/// Simple agent with a lifecycle and no return value:
/// ```
/// # use odem_rs_core::{agent::{Actions, Agent, Behavior}, simulator::Sim, config::DefaultConfig, Puck};
/// # use core::pin::pin;
/// struct Foo(usize);
///
/// impl Behavior for Foo {
///     type Output = ();
///     async fn actions(&self, sim: &Sim) -> Self::Output {}
/// }
///
/// async fn sim_main(sim: &Sim) {
///     let foo = pin!(Agent::new(Foo(3)));
///     let h = sim.activate(foo);
///     sim.advance(10.0).await;
/// }
/// ```
///
/// Simple agent with lifecycle returning a value:
/// ```
/// # use odem_rs_core::{ops::defer, agent::{Actions, Agent, Behavior}, simulator::Sim, config::DefaultConfig, Puck};
/// # use core::pin::pin;
/// struct Foo(usize);
///
/// impl Behavior for Foo {
///     type Output = f32;
///     async fn actions(&self, sim: &Sim) -> f32 {
///         self.0 as f32
///     }
/// }
///
/// async fn sim_main(sim: &Sim) {
///     let foo = pin!(Agent::new(Foo(3)));
///     let x: f32 = sim.activate(foo).await;
/// }
/// ```
///
/// # Safety Warning
///
/// - The ordering of the fields in `Agent` must not be changed. Specifically,
///   `Inner` must precede `A::Subject`. Incorrect ordering leads to undefined
///   behavior due to dangling pointers referencing `item` in `inner`.
///
/// [activated]: Sim::activate
#[pin_project::pin_project]
pub struct Agent<A: Actions> {
	/// An enumeration capturing the runtime state.
	#[pin]
	inner: Inner<A>,

	/// The shared agent data.
	#[pin]
	item: A::Subject,
}

/// Inner enumeration for the runtime states of the `Agent`: `Born`, `Bust`,
/// and `Live`.
///
/// The `Born`-state records the initialization of the agent before its
/// activation. The `Bust`-state is an intermediate state during activation
/// and can only be observed if the constructor-function of the future panics.
/// The `Live`-state is the state after initialization has occurred.
///
/// # Safety Warning
///
/// - The ordering of the fields in `Inner` must not be changed. Specifically,
///   `RootJob` must precede `Share`. Incorrect ordering leads to undefined
///   behavior due to dangling pointers referencing `share` in `job`.
#[pin_project::pin_project(
	project = StateProject,
	project_ref = StateProjectRef,
	project_replace = StateOwn
)]
enum Inner<A: Actions> {
	/// Pre-activation state containing an inert future, the agent's name,
	/// initial rank, and an incomplete job builder still missing the future.
	Born {
		/// Contains a callable function constructing a future from shared data
		/// and a simulation context.
		action: A::Action,
		/// The base name of the agent.
		name: &'static str,
		/// An optional initial rank.
		rank: Option<<A::Config as Config>::Rank>,
		/// A [`JobBuilder`] in intermediate state missing the lifecycle future.
		builder: JobBuilder<true, (), A::Settle>,
	},
	/// Intermediate state during activation of an agent. Should only be
	/// observable if `A::Action` panicked.
	Bust,
	/// Post-activation state containing a [`Job`] and the [agent data](Share)
	/// shared between all jobs contained in the agent.
	Live {
		/// Post-activation state containing a [`Job`] constructed by the
		/// action function.
		#[pin]
		job: RootJob<'static, A>,
		/// Data shared between all the [`Job`] associated with this agent.
		///
		/// This data is accessible from outside given any type of [`Puck`] by
		/// calling the [`Puck::subject`]-method and downcasting the resulting
		/// [`Any`] into a concrete type.
		#[pin]
		share: Share<A::Config>,
	},
}

/// Type-alias for the [`Future`]-type constructed by the [`Lifecycle`]-trait.
type RootFuture<'l, A> = <<A as Actions>::Action as Lifecycle<
	'l,
	<A as Actions>::Config,
	<A as Actions>::Subject,
>>::Future;

/// Type-alias for the [`Job`]-type constructed by the [`Lifecycle`]-trait.
type RootJob<'l, A> =
	Job<'static, <A as Actions>::Config, RootFuture<'l, A>, <A as Actions>::Settle>;

/// Type-alias for the [`JobPuck`]-type constructed by the [`Lifecycle`]-trait.
type RootPuck<'l, A> =
	JobPuck<'l, <A as Actions>::Config, RootFuture<'l, A>, <A as Actions>::Settle>;

impl Agent<()> {
	/// Creates a new agent with a lifecycle that produces futures that
	/// cannot fail.
	///
	/// The inability to fail refers to the property that - as far as the
	/// underlying [`Continuation`] is concerned - the agent can only either be
	/// aborted, panic, or terminate successfully, i.e. with a result of `Ok`.
	///
	/// It doesn't mean that the return type of the agent cannot be a
	/// `Result`, just that the `Err` variant is not recorded as a failure but
	/// as an acceptable (`Ok`) result of the execution.
	///
	/// If an agent with a publicly observable failure is required, use the
	/// [`Builder`] to configure it instead.
	#[track_caller]
	pub fn new<'p, C, I>(
		subject: I,
	) -> Lease<
		'p,
		Agent<
			impl Actions<Config = C, Subject = I, Output = I::Output, Settle = Unchecked> + use<C, I>,
		>,
	>
	where
		C: Config,
		I: Any + Behavior<C>,
	{
		Self::build()
			.with_name(subject.name())
			.with_subject(subject)
			.with_actions(I::actions)
			.finish()
	}

	/// Creates a [`Builder`] that can be used to configure the agent
	/// before instantiating it.
	pub const fn build<C: Config>() -> Builder<C> {
		Builder::new()
	}
}

impl<A: Actions> Deref for Agent<A> {
	type Target = A::Subject;

	fn deref(&self) -> &Self::Target {
		&self.item
	}
}

impl<A: Actions> DerefMut for Agent<A> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.item
	}
}

impl<A: Actions> Dispatch for Agent<A> {
	fn poll(self: Pin<&Self>, cx: &mut Context<'_>) -> Poll<ExitStatus> {
		match self.project_ref().inner.project_ref() {
			StateProjectRef::Live { job, .. } => job.poll(cx),
			_ => unreachable!("the agent should always be active"),
		}
	}
}

impl<A: Actions> Active<A::Config> for Agent<A> {
	type Output = A::Output;
	type Puck<'p>
		= Puck<'p, A>
	where
		Self: 'p;

	fn bind<'p>(this: Pin<LeasedMut<'p, Self>>, sctx: &'p Share<A::Config>) -> Self::Puck<'p> {
		let mut this = this.project().project();

		// replace the `Born` state by `Bust` for the transition
		match this.inner.as_mut().project_replace(Inner::Bust) {
			StateOwn::Born {
				action,
				name,
				rank,
				builder,
			} => {
				use core::mem::transmute;

				// extract the simulation context from the shared context
				let item = this.item.into_ref().get_ref();
				let sim = sctx.sim();
				let pid = sim.pid_gen::<A::Subject>();

				// create and enter the agent span
				#[cfg(feature = "tracing")]
				let _span = tracing::error_span!(
					parent: None, "Agent",
					label = %Label { name, pid: Some(pid) }
				)
				.entered();

				// construct the job by executing the action;
				// this may panic, leaving the agent in state `Bust`
				let live = builder.with_actions(action.init(item, sim)).finish();

				// overwrite the previous state, settling the created job in
				// its final memory location
				this.inner.set(Inner::Live {
					// SAFETY: Transmuting the lifetime doesn't change the
					// object layout, and we always restore the original one.
					job: unsafe {
						transmute::<RootJob<'p, A>, RootJob<'static, A>>(live.into_inner())
					},
					// SAFETY: The `item` outlives `share` due to the ordering
					// in `Inner` in combination with drop-order guarantees.
					share: unsafe {
						Share::new(
							sim.clone(),
							item,
							rank.unwrap_or(sim.config().default_rank()),
							name,
							pid,
						)
					},
				});

				match this.inner.project() {
					StateProject::Live { job, share } => {
						// SAFETY: Transmuting the lifetime `'p` of root job
						// back is safe. Adding the `LeasedMut` is safe since
						// the job is never accessed again.
						let live = unsafe {
							transmute::<
								Pin<&'p mut RootJob<'static, A>>,
								Pin<LeasedMut<'p, RootJob<'p, A>>>,
							>(job)
						};

						Puck(Active::bind(live, share.into_ref().get_ref()))
					}
					_ => unreachable!(),
				}
			}
			_ => unreachable!("the agent has already been bound, which should not be possible"),
		}
	}
}

/* ************************************************************* Agent Puck */

/// The root [`Puck`](crate::Puck) of the [`Agent`].
pub struct Puck<'p, A: Actions>(RootPuck<'p, A>);

impl<C: Config, A: Actions<Config = C>> Puck<'_, A> {
	/// Sets the rank of the root job.
	///
	/// The new rank takes immediate effect and causes the rearrangement
	/// of all jobs currently scheduled, both in the present and future.
	///
	/// Lowering the rank of the active `Agent` can lead to another `Agent`
	/// gaining control if one with a higher rank after the change has jobs
	/// scheduled at the current model time. This change takes effect once the
	/// currently active agent suspends.
	pub fn update_rank(&self, rank: C::Rank) {
		self.0.share().update_rank(rank);
	}

	/// Returns a reference to the subject.
	pub fn subject(&self) -> &A::Subject {
		use crate::Puck;

		self.0.subject().downcast_ref::<A::Subject>().unwrap()
	}

	/// Terminates the root job prematurely.
	///
	/// This method consumes the reference to prevent it from being called twice
	/// or used to extract the return value later.
	pub fn abort(self) {
		self.0.abort();
	}
}

impl<C: Config, A: Actions<Config = C>> crate::Puck<C> for Puck<'_, A> {
	fn result(&mut self) -> Option<Self::Output> {
		self.0.result()
	}

	fn wake(&mut self) -> Result<(), error::NotIdle> {
		self.0.wake()
	}

	fn subject(&self) -> &dyn Any {
		self.0.subject()
	}

	fn sim(&self) -> &Sim<C> {
		self.0.sim()
	}

	fn label(&self) -> Label {
		self.0.label()
	}

	fn time(&self) -> Option<C::Time> {
		self.0.time()
	}

	fn rank(&self) -> C::Rank {
		self.0.rank()
	}
	
	fn prec(&self) -> Prec {
		self.0.prec()
	}

	fn state(&self) -> ContState {
		self.0.state()
	}

	fn location(&self) -> &'static Location<'static> {
		self.0.location()
	}
}

impl<A: Actions> AsRef<Continuation<'static, A::Config>> for Puck<'_, A> {
	fn as_ref(&self) -> &Continuation<'static, A::Config> {
		self.0.as_ref()
	}
}

impl<A: Actions> AsIrc<Continuation<'static, A::Config>> for Puck<'_, A> {
	fn as_irc(&self) -> Irc<Continuation<'static, A::Config>> {
		self.0.as_irc()
	}
}

impl<A: Actions> IntoFuture for Puck<'_, A> {
	type Output = A::Output;
	type IntoFuture = crate::ops::Join<A::Config, Self>;

	fn into_future(self) -> Self::IntoFuture {
		crate::ops::join(self)
	}
}

/* ********************************************************** Agent Builder */

/// Used for configuring [agents](Agent) before they are instantiated.
///
/// Instantiating agents this way allows to set a custom name and initial
/// rank.
pub struct Builder<C: Config, I = (), A = (), S = Unchecked> {
	/// The shared item.
	subject: I,
	/// The lifecycle of the agent.
	actions: A,
	/// The custom name or the name derived from the type name of `I`.
	name: Option<&'static str>,
	/// The initial rank of the created agent.
	rank: Option<C::Rank>,
	/// The [`Settle`]-routine called at the end of the agent' life.
	settle: S,
	/// The [`Location`] of the lifecycle in the source code.
	location: Option<&'static Location<'static>>,
	/// Marker for the [`Config`].
	_config: PhantomData<C>,
}

impl<C: Config> Builder<C, (), ()> {
	/// Creates a new, empty process builder.
	const fn new() -> Self {
		Builder {
			subject: (),
			actions: (),
			name: None,
			rank: None,
			settle: Unchecked,
			location: None,
			_config: PhantomData,
		}
	}
}

impl<C: Config, I: Any, A, S> Builder<C, I, A, S> {
	/// Sets the shared item (the subject) that the created process operates on.
	pub fn with_subject<X: Any>(self, object: X) -> Builder<C, X, A, S> {
		Builder {
			subject: object,
			actions: self.actions,
			name: self.name,
			rank: self.rank,
			settle: self.settle,
			location: self.location,
			_config: self._config,
		}
	}

	/// Sets the lifecycle for this process builder with a future that cannot
	/// fail.
	///
	/// The inability to fail refers to the property that - as far as the
	/// underlying [`Continuation`] is concerned - the process can only either
	/// be aborted, panic, or terminate successfully, i.e. with a result of
	/// `Ok`.
	///
	/// It doesn't mean that the return type of the process cannot be a
	/// `Result`, just that the `Err` variant is not recorded as a failure but
	/// as an acceptable (`Ok`) result of the execution.
	#[track_caller]
	pub fn with_actions<X>(self, actions: X) -> Builder<C, I, X, S>
	where
		X: for<'l> Lifecycle<'l, C, I>,
	{
		Builder {
			subject: self.subject,
			actions,
			name: self.name,
			rank: self.rank,
			settle: self.settle,
			location: Some(self.location.unwrap_or_else(Location::caller)),
			_config: self._config,
		}
	}

	/// Sets an explicit source-code [Location] for the [Agent].
	///
	/// The location will otherwise default to the location of the lifecycle.
	pub const fn with_location(mut self, location: &'static Location<'static>) -> Self {
		self.location = Some(location);
		self
	}

	/// Sets a custom name for the instantiated process.
	///
	/// The name will default to the type name of the shared item.
	pub const fn with_name(mut self, name: &'static str) -> Self {
		self.name = Some(name);
		self
	}

	/// Sets a custom starting rank for the instantiated process.
	///
	/// The default rank is specified in the [configuration](Config).
	pub fn with_rank(self, rank: C::Rank) -> Self {
		Builder {
			rank: Some(rank),
			..self
		}
	}

	/// Sets a user-defined [Settle] that is run once the lifecycle of the
	/// [Agent] terminates.
	pub fn with_finalizer<X>(self, finalizer: X) -> Builder<C, I, A, X> {
		Builder {
			subject: self.subject,
			actions: self.actions,
			name: self.name,
			rank: self.rank,
			settle: finalizer,
			location: self.location,
			_config: self._config,
		}
	}

	/// Sets a specific [Settle] routine that is used to convert a `bool`,
	/// [`Option`], or [`Result`] return type into an [ExitStatus] that may
	/// be observed by other agents or jobs.
	pub fn checked(self) -> Builder<C, I, A, Checked> {
		self.with_finalizer(Checked)
	}
}

impl<C: Config, I: Any, R, A, S> Builder<C, I, A, S>
where
	A: for<'l> Lifecycle<'l, C, I, Output = R>,
	S: Settle<R>,
{
	/// Instantiates the configured `Agent` from the builder.
	/// 
	/// The produced [`Agent`] is wrapped into a [`Lease`] in order to create
	/// an indefinite borrow that is needed to prevent double-activations and
	/// allow the safe creation of intrusively counted references. Simply
	/// pinning the result will allow it to be passed to [`Sim::activate`].
	pub fn finish<'p>(self) -> Lease<'p, Agent<Action<C, I, A, S>>> {
		Lease::new(Agent {
			item: self.subject,
			inner: Inner::Born {
				action: self.actions,
				name: match self.name {
					Some(name) => name,
					_ => type_name::<I>(),
				},
				rank: self.rank,
				builder: JobBuilder::root()
					.with_finalizer(self.settle)
					.with_location(self.location.unwrap()),
			},
		})
	}
}

/* **************************************************** Trait Implementations */

impl<C, I, A, R> Behavior<C> for (I, A)
where
	C: Config,
	I: Any,
	A: Copy + for<'l> Lifecycle<'l, C, I, Output = R>,
{
	type Output = R;

	async fn actions(&self, sim: &Sim<C>) -> R {
		self.1.init(&self.0, sim).await
	}

	fn name(&self) -> &'static str {
		type_name::<I>()
	}
}

/// Marker-type that is used to determine the configuration-, instance-,
/// action-, and finalizer-types of the agent.
pub struct Action<C, I, A, T>(PhantomData<(C, I, A, T)>);

impl<C: Config, I: Any, R, A, T: Settle<R>> Actions for Action<C, I, A, T>
where
	A: for<'l> Lifecycle<'l, C, I, Output = R>,
{
	type Config = C;
	type Subject = I;
	type Output = R;
	type Settle = T;
	type Action = A;
}

impl Actions for () {
	type Config = DefaultConfig;
	type Subject = ();
	type Output = ();
	type Settle = Unchecked;
	type Action = Pending<()>;
}

impl<'p, C, I> Lifecycle<'p, C, I> for Pending<()>
where
	C: Config,
	I: Any,
{
	type Output = ();
	type Future = Pending<()>;

	fn init(self, _item: &'p I, _sim: &'p Sim<C>) -> Self::Future {
		pending()
	}
}

impl<'p, C, I, A, F> Lifecycle<'p, C, I> for A
where
	C: Config,
	I: Any,
	A: FnOnce(&'p I, &'p Sim<C>) -> F,
	F: Future + 'p,
{
	type Output = F::Output;
	type Future = F;

	fn init(self, item: &'p I, sim: &'p Sim<C>) -> Self::Future {
		self(item, sim)
	}
}