About serializers as a mechanism for dealing with concurrency

52 Views Asked by At

tl;dr

Is changing the former snippet below for the latter safe?

Complete question

In section §3.4.2 from SICP, the following function for creating an account balance able to cope with concurrent requests is shown at page 305:

(define (make-account balance)
  (define (withdraw amount)
    (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
      "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (let ((protected (make-serializer)))
    (define (dispatch m)
      (cond ((eq? m 'withdraw) (protected withdraw))
            ((eq? m 'deposit) (protected deposit))
            ((eq? m 'balance) balance)
            (else (error " Unknown request -- MAKE-ACCOUNT"
                         m))))
    dispatch))

where a serializer named protected is created via (protected (make-serializer)) in the let statement, and subsequently used to create, out of withdraw and deposit two functions that do the same thing as those two, but whose executions cannot interleave.

Later, in exercise 3.42 at page 307, the following alternative version is shown, and the question is whether the change is safe.

(define (make-account balance)
  (define (withdraw amount)
    (if (>= balance amount)
      (begin (set! balance (- balance amount))
             balance)
      "Insufficient funds"))
  (define (deposit amount)
    (set! balance (+ balance amount))
    balance)
  (let ((protected (make-serializer)))
    (let ((protected-widthdraw (protected widthdraw)) ; these lines
          (protected-deposit (protected deposit)))    ; are added
      (define (dispatch m)
        (cond ((eq? m 'withdraw) protected-withdraw)
              ((eq? m 'deposit) protected-deposit)
              ((eq? m 'balance) balance)
              (else (error " Unknown request -- MAKE-ACCOUNT"
                           m))))
      dispatch)))

My reasoning

I would be tempted to answer that yes, the change is safe, for the following reason.

In the section Serializing access to shared state, page 304, I read the following (my emphasis)

[…] serialization creates distin­guished sets of procedures such that only one execution of a procedure in each serialized set is permitted to happen at a time. […]

We can use serialization to control access to shared variables. For ex­ample, if we want to update a shared variable based on the previous value of that variable, we put the access to the previous value of the variable and the assignment of the new value to the variable in the same proce­dure. We then ensure that no other procedure that assigns to the variable can run concurrently with this procedure by serializing all of these procedures with the same serializer. […]

From this, my understanding is that given one serializer (i.e. one of the objects/entities created via make-serializer), all functions serialized via that one serializer belong to the same set, hence their operations cannot interleave with each other.

Therefore, since in the second program too, the (make-serializer) call is made only once (for each call to (make-account some-balance)), only one serializer exits in the whole program (for each…), and it goes by the name of protected, so all functions that serializers serializes end up in the same set, and so they can't step on each other's feet.

Is this conclusion correct?

1

There are 1 best solutions below

0
ignis volens On

I am fairly sure that the change is safe. In particular so long as there is exactly one serializer per account you are OK. That remains true in the modified make-account procedure: the only difference is that you're not allocating a new a member of the set of procedures that the serializer manages each time you say, for instance (account 'withdraw), but reusing one you made earlier: you just save a little allocation overhead, really.