A Modality for Safe Resource Sharing and Code

  • Slides: 18
Download presentation
A Modality for Safe Resource Sharing and Code Reentrancy Rui Shi (Yahoo! Inc) Dengping

A Modality for Safe Resource Sharing and Code Reentrancy Rui Shi (Yahoo! Inc) Dengping Zhu (Bloomberg Inc) Hongwei Xi (Boston Univ. )

The Research Context ATS is a functional programming language with a highly expressive type

The Research Context ATS is a functional programming language with a highly expressive type system rooted in the Applied Type System framework: http: //www. ats-lang. org In ATS, advanced types such as dependent types and linear types are supported. In ATS, a programming style is advocated that combines programming with theorem-proving

A Function in ATS fun{a: t@ype} swap {l 1, l 2: addr} ( pf

A Function in ATS fun{a: t@ype} swap {l 1, l 2: addr} ( pf 1: !a@l 1, pf 2: !a@l 2 | p 1: ptr l 1, p 2: ptr l 2 ) : void = let val tmp = !p 1 in !p 1 : = !p 2; !p 2 : = tmp end // end of [swap]

Resource Specification Resource protection is crucial in programming We need to properly specify resources

Resource Specification Resource protection is crucial in programming We need to properly specify resources We need to strike a balance when specifying resources If specification is too weak, verification may underachieve If specification is too strong, verification may become too demanding

Views for Classifying Capabilities (1) Given a type T and a location L, we

Views for Classifying Capabilities (1) Given a type T and a location L, we use T@L for a (primitive) view meaning that a value of the type T is stored at the location L. Given types T 1 and T 2 and a location L, we use (T 1@L) (T 2@(L+1)) for a (compound) view meaning that a value of the type T 1 is stored at L and another value of the type T 2 is stored at L+1.

Views for Classifying Capabilities (2) We can also declare recursively defined views (similar to

Views for Classifying Capabilities (2) We can also declare recursively defined views (similar to datatypes in a functional language like ML). For instance the following declaration introduces a view constructor array_v: datatype array_v (a: t@ype, int, addr) = {l: addr} array_v_nil (a, 0, l) of () | {l: addr} {n: nat} array_v_cons (a, n+1, l) of (a @ l, array_v (a, n, l+1))

Viewtypes Given a view V and a type T, we can form a viewtype

Viewtypes Given a view V and a type T, we can form a viewtype VT = V T The following type can be assigned to a function (ptrget_L) which reads from a fixed location L: (T@L, ptr(L)) (T@L T) The following type can be assigned to a function (ptrset_L) which writes to a fixed location L: (T 1@L, ptr(L), T 2) (T 2@L 1) Note that we use 1 for the unit type.

Resource Threading There is a serious problem with linear types: resources (of linear types)

Resource Threading There is a serious problem with linear types: resources (of linear types) must thread through function calls. For instance, we have seen this in the types assigned to the functions ptrget_L and ptrset_L. Suppose larray(T) is a type for linear arrays in which each element is of type T. Then the array subscripting function sub for linear arrays needs to be given the following type: larray(T) int larray(T) T So a linear array is threaded through each call to sub.

Sharable Arrays We refer to arrays in functional languages like ML as (non-linear) sharable

Sharable Arrays We refer to arrays in functional languages like ML as (non-linear) sharable arrays. The question we want to answer is how a sharable array can be implemented based on a linear array. More generally, how sharable data structures can be implemented based on linear ones.

A Sharing Modality Given a viewtype VT, we use □VT for a (non-linear) type

A Sharing Modality Given a viewtype VT, we use □VT for a (non-linear) type such that a value of the type □VT represents the handle of a box containing a value of the viewtype VT. The introduction for □ is straightforward: vbox: (VT) □VT However, the elimination for □ is tricky. Essentially, we need something like this: let □ x = □ v in … end

The “double-borrow” problem Assume that a resource stored in a box has been borrowed.

The “double-borrow” problem Assume that a resource stored in a box has been borrowed. If it is to be borrowed again before it is returned to the box, we have a “double-borrow” problem: let □ x 1 = □ v 1 in …… let □ x 2 = □ v 2 in … … end …… end

A Type-with-Effect System A pure function is one that does not borrow resources; it

A Type-with-Effect System A pure function is one that does not borrow resources; it is given a type of the following form: (VT 1, …, VT 2) 0 VT A non-pure function is one that may borrow resources; it is given a type of the following form: (VT 1, …, VT 2) 1 VT So, we can distinguish a pure function from a nonpure function at the level of types.

Implementing references (1) We use ref(T) for (non-linear) references to values of the type

Implementing references (1) We use ref(T) for (non-linear) references to values of the type T. In ATS, ref(T) is defined as typedef ref(T) = [l: addr] (vbox(T@l) | ptr l)

Implementing References (2) fun refget (r: ref(T)): <!ref> = let val (pfbox | p)

Implementing References (2) fun refget (r: ref(T)): <!ref> = let val (pfbox | p) = r; val vbox (pf) = pfbox in ptrget (pf | p) end // end of [refget]

Implement References (3) fun refset (r: ref(T), x: T): <!ref> void = let val

Implement References (3) fun refset (r: ref(T), x: T): <!ref> void = let val (pfbox | p) = r; val vbox (pf) = pfbox in ptrset (pf | p, x) end // end of [refset]

Reentrancy (1) A function f is reentrant if it can be called in a

Reentrancy (1) A function f is reentrant if it can be called in a nested manner. That is, another call to f can be made before a call to f is finished. A non-reentrant function is one that is not reentrant. For instance, functions like ctime, drand 48, and strtok are non-reentrant. Their reentrant counterparts are ctime_r, drand 48_r, and strtok_r, respectively.

Reentrancy (2) Pure functions are reentrant Non-pure functions are not reentrant However, if a

Reentrancy (2) Pure functions are reentrant Non-pure functions are not reentrant However, if a function is guaranteed to do borrowing/returning atomically, then this function is still reentrant. We assign such a function a type of the following form: (VT 1, …, VT 2) 0. 5 VT

Conclusion We have presented a type-theoretic justification for implementing (non-linear) sharable resources based on

Conclusion We have presented a type-theoretic justification for implementing (non-linear) sharable resources based on linear resources We have fully implemented the sharing modality in ATS and also used it extensively. The solution we adopted to the “double-borrow” problem is simple but effective in practice. A more elaborate solution is certainly possible but it may not fit well in ATS.