Ejemplo n.º 1
0
class SourceCalendar:
    """
    SourceCalendar organizes state for a delegation used as a
    source for client reservations. A source calendar maintains a list of
    "outlays", client reservations that have been allocated from the source
    delegation. The outlay calendar is organized by real time.
    The calendar also maintains a list of incoming extension requests for
    reservations that have been satisfied from the underlying source delegations.
    """
    def __init__(self, *, clock: ActorClock, source: ABCDelegation):
        """
        Constructor
        @params clock: clock
        @params source: source delegation
        """
        # Clock.
        self.clock = clock
        # The source delegation.
        self.source = source
        # Allocated reservations.
        self.outlays = ReservationHoldings()
        # Incoming extension requests.
        self.extending = ReservationList()

    def tick(self, *, cycle: int):
        ms = self.clock.cycle_end_in_millis(cycle=cycle)
        self.outlays.tick(time=ms)
        self.extending.tick(cycle=cycle)
 def test_add_reservation(self):
     r_list = ReservationList()
     for i in range(10):
         for j in range(10):
             c = (i * 10) + j
             res = self.make_reservation(str(c))
             r_list.add_reservation(reservation=res, cycle=i)
             self.assertEqual(c + 1, r_list.size())
 def __init__(self, *, clock: ActorClock):
     """
     Constructor
     @params clock: clock
     """
     super().__init__(clock=clock)
     self.closing = ReservationList()
     self.redeeming = ReservationList()
 def __init__(self, *, clock: ActorClock):
     """
     Creates a new instance.
     @params clock : clock factory
     """
     # List of incoming requests grouped by start cycle.
     super().__init__(clock=clock)
     self.requests = ReservationList()
     # List of reservations to be closed grouped by cycle.
     self.closing = ReservationList()
     # All currently active reservations.
     self.outlays = ReservationHoldings()
     self.lock = threading.Lock()
 def __init__(self, *, clock: ActorClock):
     """
     Constructor
     @params clock: clock factory
     """
     super().__init__(clock=clock)
     # List of reservations grouped by closing time.
     self.closing = ReservationList()
     # Reservation requests grouped by start cycle.
     self.requests = ReservationList()
     # Source reservation calendars indexed by the source reservation identifier
     # <ReservationID, SourceCalendar>
     self.sources = {}
Ejemplo n.º 6
0
 def __init__(self, *, clock: ActorClock, source: ABCDelegation):
     """
     Constructor
     @params clock: clock
     @params source: source delegation
     """
     # Clock.
     self.clock = clock
     # The source delegation.
     self.source = source
     # Allocated reservations.
     self.outlays = ReservationHoldings()
     # Incoming extension requests.
     self.extending = ReservationList()
 def __init__(self, *, clock: ActorClock):
     """
     Constructor
     @params clock: clock factory
     """
     super().__init__(clock=clock)
     # Set of reservations representing the current demand. Callers are
     # responsible for removing serviced reservations
     self.demand = ReservationSet()
     # Set of reservations for which a request has been issued but no
     # confirmation has been received. Callers are responsible for removing
     # acknowledged reservations.
     self.pending = ReservationSet()
     # Set of reservations grouped by renewing time.
     self.renewing = ReservationList()
     # Set of active reservations.
     self.holdings = ReservationHoldings()
     self.lock = threading.Lock()
    def test_remove_reservation(self):
        r_list = ReservationList()
        for i in range(10):
            for j in range(10):
                c = (i * 10) + j
                res = self.make_reservation(str(c))
                r_list.add_reservation(reservation=res, cycle=i)
                self.assertEqual(c + 1, r_list.size())
                self.assertEqual(c + 1, len(r_list.reservation_id_to_cycle))

        for i in range(10):
            for j in range(10):
                c = (i * 10) + j
                res = self.make_reservation(str(c))
                exist = self.check_exists(r_list, res, i)
                self.assertTrue(exist)
                r_list.remove_reservation(reservation=res)
                exist = self.check_exists(holdings=r_list,
                                          reservation=res,
                                          cycle=i)
                self.assertTrue(not exist)
    def test_get_reservations(self):
        r_list = ReservationList()
        for i in range(10):
            for j in range(10):
                c = (i * 10) + j
                res = self.make_reservation(str(c))
                r_list.add_reservation(reservation=res, cycle=i)
                self.assertEqual(c + 1, r_list.size())

        for i in range(10):
            low = 10 * i
            high = 10 * (i + 1)

            rset = r_list.get_reservations(cycle=i)
            self.assertIsNotNone(rset)
            self.assertEqual(10, rset.size())

            for r in rset.values():
                value = int(str(r.get_reservation_id()))
                self.assertTrue(low <= value < high)

        rset = r_list.get_reservations(cycle=10)
        self.assertIsNotNone(rset)
        self.assertEqual(0, rset.size())
class AuthorityCalendar(BaseCalendar):
    """
    An AuthorityCalendar is used to organized reservation information for an authority. It extends the functionality of
    BaseCalendar with a number of collections:
    - requests: a collection of client requests organized by the time to be serviced
    - closing: a collection of client reservations organized by closing time
    - outlays: a collection of active reservations (outlays)
    """
    def __init__(self, *, clock: ActorClock):
        """
        Creates a new instance.
        @params clock : clock factory
        """
        # List of incoming requests grouped by start cycle.
        super().__init__(clock=clock)
        self.requests = ReservationList()
        # List of reservations to be closed grouped by cycle.
        self.closing = ReservationList()
        # All currently active reservations.
        self.outlays = ReservationHoldings()
        self.lock = threading.Lock()

    def __getstate__(self):
        state = self.__dict__.copy()
        del state['lock']
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.lock = threading.Lock()

    def remove(self, *, reservation: ABCReservationMixin):
        """
        Removes the specified reservation from the calendar.
        @params reservation : reservation to remove
        """
        if isinstance(reservation, ABCServerReservation):
            self.remove_request(reservation=reservation)
            self.remove_closing(reservation=reservation)

        if isinstance(reservation, ABCAuthorityReservation):
            self.remove_outlay(reservation=reservation)

    def remove_schedule_or_in_progress(self, *,
                                       reservation: ABCReservationMixin):
        """
        Removes the specified reservations from all internal calendar data
        structures that represent operations to be scheduled in the future or
        operations that are currently in progress. Does not remove the
        reservation from the outlays list
        @params reservation: reservation to remove
        """
        if isinstance(reservation, ABCServerReservation):
            self.remove_request(reservation=reservation)
            self.remove_closing(reservation=reservation)

    def get_requests(self, *, cycle: int) -> ReservationSet:
        """
        Returns all client requests for the specified cycle.
        @params cycle:  cycle
        @returns set of requests for the cycle
        """
        try:
            self.lock.acquire()
            return self.requests.get_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_request(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a new client request.
        @params reservation: reservation to add
        @params cycle: cycle
        """
        try:
            self.lock.acquire()
            self.requests.add_reservation(reservation=reservation, cycle=cycle)
        finally:
            self.lock.release()

    def remove_request(self, *, reservation: ABCReservationMixin):
        """
        Removes the specified reservation from the request list.
        @params reservation: reservation to remove
        """
        try:
            self.lock.acquire()
            self.requests.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def get_closing(self, *, cycle: int) -> ReservationSet:
        """
        Returns all reservations scheduled for closing at the specified cycle.
        @params cycle: cycle
        @returns set of reservations scheduled for closing at the cycle
        """
        try:
            self.lock.acquire()
            result = self.closing.get_all_reservations(cycle=cycle)
            return result
        finally:
            self.lock.release()

    def add_closing(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a reservation to the closing list.
        @params reservation: reservation to add
        @params cycle: cycle
        """
        try:
            self.lock.acquire()
            self.closing.add_reservation(reservation=reservation, cycle=cycle)
        finally:
            self.lock.release()

    def remove_closing(self, *, reservation: ABCReservationMixin):
        """
        Removes the specified reservation from the closing list.
        @params reservation: reservation to remove
        """
        try:
            self.lock.acquire()
            self.closing.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def add_outlay(self, *, reservation: ABCReservationMixin, start: datetime,
                   end: datetime):
        """
        Adds an allocated client reservation.
        @params reservation: reservation to add
        @params start: start time
        @params end: end time
        """
        try:
            self.lock.acquire()
            self.outlays.add_reservation(
                reservation=reservation,
                start=ActorClock.to_milliseconds(when=start),
                end=ActorClock.to_milliseconds(when=end))
        finally:
            self.lock.release()

    def remove_outlay(self, *, reservation: ABCReservationMixin):
        """
        Removes a reservation from the outlays list.
        @params reservation: reservation to remove
        """
        try:
            self.lock.acquire()
            self.outlays.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def get_outlays(self, *, d: datetime = None) -> ReservationSet:
        """
        Returns the active client reservations.
        @returns set of all active client reservations
        """
        try:
            self.lock.acquire()
            if d is None:
                return self.outlays.get_reservations()
            else:
                return self.outlays.get_reservations(
                    time=ActorClock.to_milliseconds(when=d))
        finally:
            self.lock.release()

    def tick(self, *, cycle: int):
        try:
            self.lock.acquire()
            super().tick(cycle=cycle)
            self.requests.tick(cycle=cycle)
            self.closing.tick(cycle=cycle)

            ms = self.clock.cycle_end_in_millis(cycle=cycle)
            self.outlays.tick(time=ms)
        finally:
            self.lock.release()
    def test_tick(self):
        r_list = ReservationList()
        for i in range(10):
            for j in range(10):
                c = (i * 10) + j
                res = self.make_reservation(str(c))
                r_list.add_reservation(reservation=res, cycle=i)
                self.assertEqual(c + 1, r_list.size())
                self.assertEqual(c + 1, len(r_list.reservation_id_to_cycle))

        for i in range(10):
            rset = r_list.get_reservations(cycle=i)
            self.assertIsNotNone(rset)
            self.assertEqual(10, rset.size())

            expected_size = (10 - i) * 10
            self.assertEqual(expected_size, r_list.size())
            r_list.tick(cycle=i)

            rset = r_list.get_reservations(cycle=i)

            self.assertIsNotNone(rset)
            self.assertEqual(0, rset.size())

        self.assertEqual(0, r_list.size())
        self.assertEqual(0, len(r_list.reservation_id_to_cycle))
 def test_create(self):
     r_list = ReservationList()
     self.assertEqual(r_list.size(), 0)
     self.assertIsNotNone(r_list.rset_wrapper_list)
     self.assertIsNotNone(r_list.cycle_to_rset)
     self.assertEqual(r_list.count, 0)
 def check_exists(self, holdings: ReservationList,
                  reservation: ABCReservationMixin, cycle: int):
     rset = holdings.get_reservations(cycle=cycle)
     return rset.contains(reservation=reservation)
class ClientCalendar(BaseCalendar):
    """
    This a client-side calendar to be used by brokers  or service managers. A client
    calendar maintains the following lists:
    - demand: a list of reservations representing demand for resources
    - pending: a list of reservations with pending operations
    - renewing: a list of reservations organized by the time they must be renewed (cycle)
    - holdings: a list of active/granted reservations associated with their lease term.
      This list should be maintained using real time, not cycles.

    The renewing and holding lists are automatically purged by the implementation as
    time advances. The demand and pending list, however, must be purged manually by the
    user of this class.
    """
    def __init__(self, *, clock: ActorClock):
        """
        Constructor
        @params clock: clock factory
        """
        super().__init__(clock=clock)
        # Set of reservations representing the current demand. Callers are
        # responsible for removing serviced reservations
        self.demand = ReservationSet()
        # Set of reservations for which a request has been issued but no
        # confirmation has been received. Callers are responsible for removing
        # acknowledged reservations.
        self.pending = ReservationSet()
        # Set of reservations grouped by renewing time.
        self.renewing = ReservationList()
        # Set of active reservations.
        self.holdings = ReservationHoldings()
        self.lock = threading.Lock()

    def __getstate__(self):
        state = self.__dict__.copy()
        del state['lock']
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.lock = threading.Lock()

    def remove(self, *, reservation: ABCReservationMixin):
        """
        Removes the specified reservation from all internal calendar data structures
        @params reservation: reservation to remove
        """
        self.remove_demand(reservation=reservation)
        self.remove_pending(reservation=reservation)
        self.remove_renewing(reservation=reservation)
        self.remove_holdings(reservation=reservation)

    def remove_scheduled_or_in_progress(self, *,
                                        reservation: ABCReservationMixin):
        """
        Removes the specified reservations from all internal calendar data
        structures that represent operations to be scheduled in the future or
        operations that are currently in progress. Does not remove the
        reservation from the holdings list.
        @params reservation : reservation to remove
        """
        self.remove_demand(reservation=reservation)
        self.remove_pending(reservation=reservation)
        self.remove_renewing(reservation=reservation)

    def get_demand(self) -> ReservationSet:
        """
        Returns the known demand. Can be for resources starting at different times.
        @returns the set of demanded reservations.
        """
        try:
            self.lock.acquire()
            return self.demand.clone()
        finally:
            self.lock.release()

    def add_demand(self, *, reservation: ABCReservationMixin):
        """
        Adds a reservation to the demand list.
        @params reservation: reservation to add
        """
        try:
            self.lock.acquire()
            self.demand.add(reservation=reservation)
        finally:
            self.lock.release()

    def remove_demand(self, *, reservation: ABCReservationMixin):
        """
        Removes a reservation to the demand list.
        @params reservation: reservation to remove
        """
        try:
            self.lock.acquire()
            self.demand.remove(reservation=reservation)
        finally:
            self.lock.release()

    def get_pending(self) -> ReservationSet:
        """
        Returns the known pending. Can be for resources starting at different times.
        @returns the set of pending reservations.
        """
        try:
            self.lock.acquire()
            return self.pending.clone()
        finally:
            self.lock.release()

    def add_pending(self, *, reservation: ABCReservationMixin):
        """
        Adds a reservation to the pending list.
        @params reservation: reservation to add
        """
        try:
            self.lock.acquire()
            self.pending.add(reservation=reservation)
        finally:
            self.lock.release()

    def remove_pending(self, *, reservation: ABCReservationMixin):
        """
        Removes a reservation to the pending list.
        @params reservation: reservation to remove
        """
        try:
            self.lock.acquire()
            self.pending.remove(reservation=reservation)
        finally:
            self.lock.release()

    def get_renewing(self, *, cycle: int) -> ReservationSet:
        """
        Returns the reservations that need to be renewed on the specified cycle.
        @params cycle : cycle number
        @returns reservation set with reservations to be renewed on the specified cycle
        """
        try:
            self.lock.acquire()
            return self.renewing.get_all_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_renewing(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a reservation to the renewing list at the given cycle.
        @params reservation : reservation to add
        @params cycle : cycle number
        """
        try:
            self.lock.acquire()
            self.renewing.add_reservation(reservation=reservation, cycle=cycle)
        finally:
            self.lock.release()

    def remove_renewing(self, *, reservation: ABCReservationMixin):
        """
        Removes the reservation from the renewing list.
        @params reservation : reservation to remove
        """
        try:
            self.lock.acquire()
            self.renewing.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def get_holdings(self,
                     *,
                     d: datetime = None,
                     type: ResourceType = None) -> ReservationSet:
        """
        Returns the resources of the specified type held by the client that are active at the specified time instance.
        @params d : datetime instance.
        @params type : resource type
        @returns st of reservations of the specified type that are active at the specified time
        """
        try:
            self.lock.acquire()
            when = None
            if d is not None:
                when = ActorClock.to_milliseconds(when=d)
            return self.holdings.get_reservations(time=when, rtype=type)
        finally:
            self.lock.release()

    def add_holdings(self, *, reservation: ABCReservationMixin,
                     start: datetime, end: datetime):
        """
        Adds a reservation to the holdings list.
        @params reservation : reservation to add
        @params start : start time
        @params end : end time
        """
        try:
            self.lock.acquire()
            self.holdings.add_reservation(
                reservation=reservation,
                start=ActorClock.to_milliseconds(when=start),
                end=ActorClock.to_milliseconds(when=end))
        finally:
            self.lock.release()

    def remove_holdings(self, *, reservation: ABCReservationMixin):
        """
        Removes the reservation from the renewing list.
        @params reservation : reservation to remove
        """
        try:
            self.lock.acquire()
            self.holdings.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def tick(self, *, cycle: int):
        try:
            self.lock.acquire()
            super().tick(cycle=cycle)
            self.renewing.tick(cycle=cycle)
            ms = self.clock.cycle_end_in_millis(cycle=cycle)
            self.holdings.tick(time=ms)
        finally:
            self.lock.release()
class ControllerCalendar(ClientCalendar):
    """
    Controller calendar. In addition to the lists maintained by ClientCalendar,
    this class maintains the following lists:
     - closing: a list of reservations organized by the cycle they must be closed
     - redeeming: a list of reservations organized by the cycle they must be redeemed
    """
    def __init__(self, *, clock: ActorClock):
        """
        Constructor
        @params clock: clock
        """
        super().__init__(clock=clock)
        self.closing = ReservationList()
        self.redeeming = ReservationList()

    def remove(self, *, reservation: ABCReservationMixin):
        """
        Removes the reservation from the calendar.

        @params reservation: reservation to remove
        """
        super().remove(reservation=reservation)
        self.remove_closing(reservation=reservation)
        self.remove_redeeming(reservation=reservation)

    def get_closing(self, *, cycle: int) -> ReservationSet:
        """
        Returns all reservations that need to be closed up to and including the specified cycle.

        @params cycle : cycle

        @returns a set of reservations that must be closed on the specified cycle
        """
        try:
            self.lock.acquire()
            return self.closing.get_all_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_closing(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a reservation to be closed on the specified cycle.

        @params reservation : reservation to add
        @params cycle : cycle
        """
        try:
            self.lock.acquire()
            self.closing.add_reservation(reservation=reservation, cycle=cycle)
        finally:
            self.lock.release()

    def remove_closing(self, *, reservation: ABCReservationMixin):
        """
         Removes the given reservation from the closing list.

        @params reservation : reservation to remove
        """
        try:
            self.lock.acquire()
            self.closing.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def get_redeeming(self, *, cycle: int) -> ReservationSet:
        """
        Returns all reservations that need to be redeemed up to and including the specified cycle.

        @params cycle : cycle

        @returns a set of reservations that must be redeemed on the specified cycle
        """
        try:
            self.lock.acquire()
            return self.redeeming.get_all_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_redeeming(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a reservation to be redeeming on the specified cycle.

        @params reservation : reservation to add
        @params cycle : cycle
        """
        try:
            self.lock.acquire()
            self.redeeming.add_reservation(reservation=reservation,
                                           cycle=cycle)
        finally:
            self.lock.release()

    def remove_redeeming(self, *, reservation: ABCReservationMixin):
        """
         Removes the given reservation from the redeeming list.

        @params reservation : reservation to remove
        """
        try:
            self.lock.acquire()
            self.redeeming.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def tick(self, *, cycle: int):
        super().tick(cycle=cycle)
        try:
            self.lock.acquire()
            self.closing.tick(cycle=cycle)
            self.redeeming.tick(cycle=cycle)
        finally:
            self.lock.release()
class BrokerCalendar(ClientCalendar):
    """
    BrokerCalendar used to organize reservation information for a broker. It builds on the functionality provided by
    ClientCalendar and extends it with the following lists:
    - closing: list of reservations organized by closing time
    - requests: list of incoming requests
    - source calendars for each source reservation
    """
    def __init__(self, *, clock: ActorClock):
        """
        Constructor
        @params clock: clock factory
        """
        super().__init__(clock=clock)
        # List of reservations grouped by closing time.
        self.closing = ReservationList()
        # Reservation requests grouped by start cycle.
        self.requests = ReservationList()
        # Source reservation calendars indexed by the source reservation identifier
        # <ReservationID, SourceCalendar>
        self.sources = {}

    def remove(self, *, reservation: ABCReservationMixin):
        super().remove(reservation=reservation)
        self.remove_closing(reservation=reservation)
        if isinstance(reservation, ABCBrokerReservation):
            self.remove_request(reservation=reservation)

            source = reservation.get_source()
            if source is not None:
                self.remove_request(reservation=reservation, source=source)
                self.remove_outlay(source=source, client=reservation)

    def remove_scheduled_or_in_progress(self, *,
                                        reservation: ABCReservationMixin):
        super().remove_scheduled_or_in_progress(reservation=reservation)
        self.remove_closing(reservation=reservation)

        if isinstance(reservation, ABCBrokerReservation):
            self.remove_request(reservation=reservation)

            source = reservation.get_source()
            if source is not None:
                self.remove_request(reservation=reservation, source=source)

    def get_requests(self, *, cycle: int) -> ReservationSet:
        """
        Returns all client requests for the given cycle.
        @params cycle: cycle
        @returns set of reservations representing requests starting at the specified cycle
        """
        try:
            self.lock.acquire()
            return self.requests.get_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def get_all_requests(self, *, cycle: int) -> ReservationSet:
        """
        Returns all client requests up the the given cycle.
        @params cycle: cycle
        @returns set of reservations representing requests with start time no later than cycle
        """
        try:
            self.lock.acquire()
            return self.requests.get_all_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_request(self,
                    *,
                    reservation: ABCReservationMixin,
                    cycle: int,
                    source: ABCDelegation = None):
        """
        Adds a client request.

        @params reservation: client request
        @params cycle: start cycle
        @params source: source reservation
        """
        try:
            self.lock.acquire()
            if source is None:
                self.requests.add_reservation(reservation=reservation,
                                              cycle=cycle)
            else:
                calendar = self.get_source_calendar(source=source)
                calendar.extending.add_reservation(reservation=reservation,
                                                   cycle=cycle)
        finally:
            self.lock.release()

    def get_request(self, *, source: ABCDelegation,
                    cycle: int) -> ReservationSet:
        """
        Returns the extending requests for the given source reservation.

        @params source: source reservation
        @params cycle: cycle number

        @returns set of extending reservation requests for the given source at
                the specified cycle
        """
        try:
            self.lock.acquire()
            calendar = self.get_source_calendar(source=source)
            return calendar.extending.get_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def remove_request(self,
                       *,
                       reservation: ABCReservationMixin,
                       source: ABCDelegation = None):
        """
        Removes the specified reservation from the requests list.
        @params reservation:  reservation to remove
        @params source: source reservation
        """
        try:
            self.lock.acquire()
            if source is not None:
                calendar = self.get_source_calendar(source=source)
                calendar.extending.remove_reservation(reservation=reservation)
            else:
                self.requests.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def add_outlay(self, *, source: ABCDelegation, client: ABCReservationMixin,
                   start: datetime, end: datetime):
        """
         Adds an outlay reservation.

        @params source: source reservation
        @params client: reservation to add
        @params start: start time
        @params end: start time
        """
        try:
            self.lock.acquire()
            calendar = self.get_source_calendar(source=source)
            calendar.outlays.add_reservation(
                reservation=client,
                start=ActorClock.to_milliseconds(when=start),
                end=ActorClock.to_milliseconds(when=end))
        finally:
            self.lock.release()

    def remove_outlay(self, *, source: ABCDelegation,
                      client: ABCReservationMixin):
        """
        Removes an outlay reservation.

        @params source : source reservation
        @params client : client reservation to be removed
        """
        try:
            self.lock.acquire()
            calendar = self.get_source_calendar(source=source)
            calendar.outlays.remove_reservation(reservation=client)
        finally:
            self.lock.release()

    def add_source(self, *, source: ABCDelegation):
        """
        Adds a source reservation. Creates a placeholder if necessary
        and adds the reservation to the holdings list.

        @params source:  source reservation
        """
        term = None
        try:
            self.lock.acquire()
            self.get_source_calendar(source=source)
            term = source.get_term()
        finally:
            self.lock.release()
        self.add_holdings(reservation=source,
                          start=term.get_new_start_time(),
                          end=term.get_end_time())

    def get_source_calendar(self, *, source: ABCDelegation) -> SourceCalendar:
        """
        Returns the outlay calendar for the given source reservation.

        @params source : source reservation

        @returns source calendar
        """
        calendar = self.sources.get(source.get_delegation_id())
        if calendar is None:
            calendar = SourceCalendar(clock=self.clock, source=source)
            self.sources[source.get_delegation_id()] = calendar
        return calendar

    def remove_source_calendar(self, *, source: ABCDelegation):
        """
        Removes any data structures associated with a source
        reservation.

        @params source : source reservation
        """
        try:
            self.lock.acquire()
            if source.get_delegation_id() in self.sources:
                self.sources.pop(source.get_delegation_id())
        finally:
            self.lock.release()

    def get_outlays(self,
                    *,
                    source: ABCDelegation,
                    time: datetime = None) -> ReservationSet:
        """
        Returns the client reservations satisfied from the given source
        reservation at the specified time.

        @params source : source reservation
        @params time:  time instance

        @returns set of client reservations satisfied from the given source at the specified time.
        """
        try:
            self.lock.acquire()
            calendar = self.get_source_calendar(source=source)
            if time is None:
                return calendar.outlays.get_reservations()
            else:
                return calendar.outlays.get_reservations(
                    time=ActorClock.to_milliseconds(when=time))
        finally:
            self.lock.release()

    def get_closing(self, *, cycle: int) -> ReservationSet:
        """
        Returns all reservations that need to be closed on the specified
        cycle.

        @params cycle : cycle

        @returns a set of reservations to be closed on the specified cycle
        """
        try:
            self.lock.acquire()
            return self.closing.get_all_reservations(cycle=cycle)
        finally:
            self.lock.release()

    def add_closing(self, *, reservation: ABCReservationMixin, cycle: int):
        """
        Adds a reservation to be closed on the specified cycle

        @params reservation : reservation to close
        @params cycle : cycle
        """
        try:
            self.lock.acquire()
            self.closing.add_reservation(reservation=reservation, cycle=cycle)
        finally:
            self.lock.release()

    def remove_closing(self, *, reservation: ABCReservationMixin):
        """
        Removes the specified reservation from the list of closing
        reservations.

        @params reservation : reservation to remove
        """
        try:
            self.lock.acquire()
            self.closing.remove_reservation(reservation=reservation)
        finally:
            self.lock.release()

    def tick(self, *, cycle: int):
        super().tick(cycle=cycle)
        try:
            self.lock.acquire()
            self.requests.tick(cycle=cycle)
            self.closing.tick(cycle=cycle)

            for calendar in self.sources.values():
                calendar.tick(cycle=cycle)
        finally:
            self.lock.release()