180 likes | 194 Views
This research paper explores a modality in ATS programming language that combines programming with theorem-proving for safe resource sharing and code reentrancy. It introduces concepts like views, view types, sharing modality, and implements references in a linear type system.
E N D
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 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 {l1,l2:addr} (pf1: !a@l1, pf2: !a@l2 | p1: ptr l1, p2: ptr l2) : void = let val tmp = !p1 in !p1 := !p2; !p2 := tmp end // end of [swap]
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 use T@L for a (primitive) view meaning that a value of the type T is stored at the location L. • Given types T1 and T2 and a location L, we use(T1@L)(T2@(L+1))for a (compound) view meaning that a value of the type T1 is stored at L and another value of the type T2 is stored at L+1.
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 VT = VT • 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: (T1@L, ptr(L), T2) (T2@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) 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) TSo 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 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 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. If it is to be borrowed again before it is returned to the box, we have a “double-borrow” problem:let □ x1 = □ v1 in … … let □ x2 = □ v2 in … … end … …end
A Type-with-Effect System • A pure function is one that does not borrow resources; it is given a type of the following form: (VT1, …, VT2) 0 VT • A non-pure function is one that may borrow resources; it is given a type of the following form: (VT1, …, VT2) 1 VT • So, we can distinguish a pure function from a non-pure function at the level of types.
Implementing references (1) • We use ref(T) for (non-linear) references to values of the type T. In ATS, ref(T) is defined astypedef ref(T) = [l:addr] (vbox(T@l) | ptr l)
Implementing References (2) fun refget (r: ref(T)):<!ref> = let val (pfbox | p) = r; val vbox (pf) = pfboxin ptrget (pf | p) end // end of [refget]
Implement References (3) fun refset (r: ref(T), x:T):<!ref> void = let val (pfbox | p) = r; val vbox (pf) = pfboxin ptrset (pf | p, x) end // end of [refset]
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, drand48, and strtok are non-reentrant. Their reentrant counterparts are ctime_r, drand48_r, and strtok_r, respectively.
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: (VT1, …, VT2) 0.5 VT
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.