/atlib/branches/proteus_annotations/tags/at2dist090708/at/lang/ambientrefs.at

http://ambienttalk.googlecode.com/ · Unknown · 708 lines · 631 code · 77 blank · 0 comment · 0 complexity · 95216d6c54e73dc142334fdf15b81c44 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * (c) Programming Technology Lab, 2006 - 2007
  4. * Authors: Tom Van Cutsem & Stijn Mostinckx
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use,
  10. * copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the
  12. * Software is furnished to do so, subject to the following
  13. * conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be
  16. * included in all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  20. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  22. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  23. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  25. * OTHER DEALINGS IN THE SOFTWARE.
  26. */
  27. /*
  28. * This module implements revised ambient references where message delivery
  29. * policies are expressed at the messaging level rather than at the
  30. * reference level.
  31. *
  32. * An ambient reference is a time, synchronisation and space-decoupled reference.
  33. * It extends far references with anonymous and group communication.
  34. *
  35. * An ambient reference is a time, synchronisation and space-decoupled reference.
  36. * It extends far references with anonymous and group communication.
  37. *
  38. * An ambient reference does not refer to one specific object, but rather
  39. * to a set of objects, known as its reach. The reach can be described
  40. * by means of a type tag or protocol (and optionally a filter).
  41. * The filter MUST be serialisable, as it is sent to remote clients for remote evaluation!
  42. *
  43. * Messages can be sent to one or all objects in range.
  44. * Messages can be buffered such that they can be sent to objects
  45. * that enter the ambient reference's range at a later point in time.
  46. * Such buffered messages can be annotated with an expiration period
  47. * such that they can be eventually removed.
  48. *
  49. * An ambient reference can be considered a little publish/subscribe
  50. * engine of its own. The reference can be used by objects to
  51. * publish messages into the environment. In order to receive such
  52. * messages, objects must register themselves by declaring themselves
  53. * to be 'in range' of the appropriate ambient reference.
  54. *
  55. * An Ambient Reference denotes an abstract set of proximate objects.
  56. *
  57. * It supports different kinds of messaging strategies:
  58. * - message arity:
  59. * - @One: choose a single receiver from the set, returns future [default]
  60. * - @All: broadcast msg to all receivers in set, returns multifuture
  61. * - message lifetime:
  62. * - @Instant: potential receivers are the current principals only,
  63. * if no match message is lost.
  64. * - @Transient(timeout): potential receivers are current principals +
  65. * all principals in scope between now and the timeout period.
  66. * If no match, buffer message until timeout.
  67. * - @Sustain: potential receivers are the current principals or any other principal
  68. * joining later. Message can be retracted explicitly via its future. [default]
  69. * - message reply destination:
  70. * - @Reply: attaches a future to the message to consume result(s) [default]
  71. * - @Oneway: no return values are required
  72. * - @Due(timeout): attaches a future to the message which, after it has been sent
  73. * to one or more receivers, may time out. Note that the timeout period only starts
  74. * when the message has an actual receiver, not when it is sent to the AR itself.
  75. *
  76. * - @Expires(timeout): a combination of @Transient(timeout) and @Due(timeout)
  77. * But not the same as just combining these. It sets the timeout period of each outgoing
  78. * message to (timeout - (time_transmitted - time_sent)), meaning that the longer it
  79. * took to discover a receiver, the less time the future gets to wait for the reply.
  80. *
  81. * Defaults can be changed at the reference level.
  82. * Every message send to an ambient reference returns a 'handle' object which
  83. * can be used to 'retract' the message send (by invoking its cancel method).
  84. * This is useful for explicitly stopping @Sustain message sends.
  85. *
  86. * Useful operations:
  87. * - snapshot: AR -> table of far refs
  88. *
  89. * Objects are exported:
  90. * - by Type or protocol
  91. * - by Type or protocol with properties
  92. *
  93. * To be investigated:
  94. * - dealing with 'dropped' or 'lost' messages: e.g. @Instant + @Reply and no one in range -> ?
  95. * - for @One messaging, consider a ranking policy to determine the most 'suitable' service in range.
  96. * - a 'metaobject protocol' to enable custom attributes (or just a more open implementation)
  97. *
  98. * Implementation-wise:
  99. * Important optimization: change the MOP of createMessage to return a residual function
  100. * Then, using partial evaluation, we can construct an optimized message constructor
  101. * that attaches the different types as indices into a table for quick dispatch
  102. * ('virtual function tables' for AR messages via partial evaluation!)
  103. * => would also be useful for futures! @Due,@Oneway,@FutureMessage processing
  104. *
  105. * @author tvcutsem
  106. */
  107. def Util := /.at.support.util;
  108. def FuturesModule := /.at.lang.futures;
  109. def MFuturesModule := /.at.lang.multifutures;
  110. def OneWayMessage := FuturesModule.OneWayMessage;
  111. def ProtocolM := /.at.lang.structuraltypes;
  112. import /.at.support.timer;
  113. import /.at.lang.firstclassrefs;
  114. deftype IllegalAnnotation <: /.at.types.Exception;
  115. def XIllegalAnnotation := /.at.exceptions.createException(IllegalAnnotation);
  116. deftype ARAnnotation;
  117. deftype MsgArity <: ARAnnotation;
  118. deftype One <: MsgArity;
  119. deftype All <: MsgArity;
  120. deftype MsgDiscoveryLifetime <: ARAnnotation;
  121. deftype Instant <: MsgDiscoveryLifetime;
  122. deftype TransientT <: MsgDiscoveryLifetime;
  123. deftype Sustain <: MsgDiscoveryLifetime;
  124. def Transient(period) {
  125. extend: TransientT with: { |period|
  126. def ==(other) { super == other };
  127. } taggedAs: [/.at.types.Isolate, /.at.types.TypeTag ];
  128. };
  129. deftype MsgCommunicationLifetime <: ARAnnotation;
  130. deftype Oneway <: MsgCommunicationLifetime;
  131. deftype Twoway <: MsgCommunicationLifetime;
  132. deftype Reply <: Twoway;
  133. deftype DueT <: Twoway;
  134. def Due(timeout) {
  135. extend: DueT with: { |timeout|
  136. def ==(other) { super == other };
  137. } taggedAs: [/.at.types.Isolate, /.at.types.TypeTag ];
  138. };
  139. deftype Expirable <: TransientT, DueT;
  140. def Expires(timeout) {
  141. extend: Expirable with: { |timeout|
  142. def ==(other) { super == other };
  143. } taggedAs: [/.at.types.Isolate, /.at.types.TypeTag ];
  144. };
  145. def extendWithHandle(msg, handle) { extend: msg with: {|handle|} };
  146. def AmbientRefsModule(ARImplModule := /.at.ambient.ar_extensional_impl) {
  147. // a pass-by-copy object that resolves to the correct implementation module when deserialised
  148. def ARImplModuleCapsule := ARImplModule.capsule;
  149. def arityTraitFor(annotation) {
  150. // ARITY = ONE | ALL
  151. if: (annotation.isSubtypeOf(One)) then: {
  152. ARImplModule.TOne;
  153. } else: {
  154. if: (annotation.isSubtypeOf(All)) then: {
  155. ARImplModule.TAll;
  156. } else: {
  157. raise: XIllegalAnnotation.new("Illegal arity annotation: " + annotation);
  158. }
  159. };
  160. };
  161. def commLTTraitFor(annotation) {
  162. // REPLY = DUE | ONEWAY | FUTURE
  163. if: (annotation.isSubtypeOf(Oneway)) then: {
  164. TOneway;
  165. } else: {
  166. if: (annotation.isSubtypeOf(DueT)) then: {
  167. makeTDue(annotation.timeout);
  168. } else: {
  169. if: (annotation.isSubtypeOf(Reply)) then: {
  170. TReply;
  171. } else: {
  172. raise: XIllegalAnnotation.new("Illegal commLT annotation: " + annotation);
  173. }
  174. }
  175. };
  176. };
  177. def discLTTraitFor(annotation) {
  178. // LIFETIME = INSTANT | TRANSIENT | SUSTAINABLE
  179. if: (annotation.isSubtypeOf(Instant)) then: {
  180. ARImplModule.TInstant;
  181. } else: {
  182. if: (annotation.isSubtypeOf(TransientT)) then: {
  183. ARImplModule.makeTTransient(annotation.period)
  184. } else: {
  185. if: (annotation.isSubtypeOf(Sustain)) then: {
  186. ARImplModule.TSustain;
  187. } else: {
  188. raise: XIllegalAnnotation.new("Illegal discLT annotation: " + annotation);
  189. }
  190. }
  191. };
  192. };
  193. def createHandler(msg, dfltArityType, dfltLifetimeType, dfltReplyType) {
  194. def ArityTrait := arityTraitFor( (is: msg taggedAs: MsgArity).ifTrue: {
  195. Util.getAnnotationOfType(msg, MsgArity);
  196. } ifFalse: { dfltArityType });
  197. def CommLTTrait;
  198. def DiscLTTrait;
  199. // EXPIRABLE => LIFETIME = TRANSIENT AND REPLY = DUE
  200. if: (is: msg taggedAs: Expirable) then: {
  201. def ann := Util.getAnnotationOfType(msg, Expirable);
  202. DiscLTTrait := makeTExpires(ann.timeout);
  203. CommLTTrait := nil;
  204. } else: {
  205. DiscLTTrait := discLTTraitFor((is: msg taggedAs: MsgDiscoveryLifetime).ifTrue: {
  206. Util.getAnnotationOfType(msg, MsgDiscoveryLifetime);
  207. } ifFalse: { dfltLifetimeType });
  208. CommLTTrait := commLTTraitFor((is: msg taggedAs: MsgCommunicationLifetime).ifTrue: {
  209. Util.getAnnotationOfType(msg, MsgCommunicationLifetime);
  210. } ifFalse: { dfltReplyType });
  211. };
  212. // perform trait composition to construct the handler
  213. object: {
  214. import ArityTrait;
  215. import CommLTTrait;
  216. import DiscLTTrait;
  217. };
  218. };
  219. def TOneway := object: {
  220. def attachFuture(msg) { [nil,msg] };
  221. def discoveryStopped(msg) { /* do nothing */ };
  222. };
  223. def TReply := object: {
  224. def attachFuture(msg) {
  225. def [fut, res] := self.createFuture(); // dispatch TArity
  226. [fut, FuturesModule.futurize(msg, fut) ];
  227. };
  228. def discoveryStopped(msg) { /* do nothing */ };
  229. };
  230. def makeTDue(initDuePeriod) {
  231. extend: TReply with: {
  232. def timeLeftForReply(msg) { initDuePeriod };
  233. def discoveryStopped(msg) {
  234. when: self.timeLeftForReply(msg) elapsed: {
  235. (msg.handle.future)<-becomeResolved()@[OneWayMessage,MetaMessage];
  236. };
  237. };
  238. };
  239. };
  240. def makeTExpires(expPeriod) {
  241. object: {
  242. import ARImplModule.makeTTransient(expPeriod);
  243. import makeTDue(expPeriod) exclude timeLeftForReply;
  244. def timeLeftForReply(msg) {
  245. self.transientPeriod - (now() - msg.handle.sendTime);
  246. };
  247. }
  248. };
  249. deftype AmbientReference;
  250. def makeAmbientReference(typetagOrProtocol,
  251. filter,
  252. defaultArity,
  253. defaultLifetime,
  254. defaultReply) {
  255. def extendedMirror;
  256. def commonMirror := mirror: {
  257. import TEventualRef;
  258. // == provide required methods for TEventualRef trait ==
  259. def intercept(message) {
  260. def handler := createHandler(message,defaultArity,defaultLifetime,defaultReply);
  261. def [fut, newMsg] := handler.attachFuture(message);
  262. def registration;
  263. def handle := object: {
  264. def future := fut;
  265. def sendTime := now();
  266. def cancel() { registration.cancel() };
  267. };
  268. registration := handler.dispatch(extendedMirror, extendWithHandle(newMsg, handle));
  269. handle // value of ambient message send is always a handle
  270. };
  271. def toString() {
  272. "ambient:"+typetagOrProtocol.typeName;
  273. };
  274. def transportStrategy :=
  275. { |ARImplModuleCapsule,typetagOrProtocol,filter,defaultArity,defaultLifetime,defaultReply|
  276. // when arriving at the remote host, create a new local ambient reference
  277. /.at.lang.ambientrefs(ARImplModuleCapsule).ambient: typetagOrProtocol
  278. where: filter
  279. withArity: defaultArity
  280. withLifetime: defaultLifetime
  281. withReply: defaultReply };
  282. }; // end mirror
  283. extendedMirror := ARImplModule.extendAmbientReference(commonMirror, typetagOrProtocol, filter);
  284. object: { } taggedAs: [ AmbientReference ] mirroredBy: extendedMirror;
  285. }; // end makeAmbientReference
  286. def dfltFilter := script: { |o| true } carrying: [];
  287. def dfltArity := One;
  288. def dfltLifetime := Sustain;
  289. def dfltReply := Oneway;
  290. // the public interface of the AR language module
  291. def PublicInterface := object: {
  292. def IllegalAnnotation := IllegalAnnotation;
  293. def One := One;
  294. def All := All;
  295. def Instant := Instant;
  296. def Transient := &Transient;
  297. def Sustain := Sustain;
  298. def Reply := Reply;
  299. def Due := &Due;
  300. def Oneway := Oneway;
  301. def Expires := &Expires;
  302. // allow exporting an object together with a set of properties
  303. def export: serviceObject as: typetagOrProtocol with: closure {
  304. def attributes := isolate: closure;
  305. ARImplModule.exportServiceObject(serviceObject, typetagOrProtocol, attributes);
  306. };
  307. // override export:as: to include a property object
  308. def export: serviceObject as: type {
  309. export: serviceObject as: type with: { nil };
  310. };
  311. def export: svcObject {
  312. export: svcObject as: (ProtocolM.protocolOf: svcObject) with: { nil };
  313. };
  314. def export: svcObject with: clo {
  315. export: svcObject as: (ProtocolM.protocolOf: svcObject) with: clo;
  316. };
  317. // def [tFuture, discoverySubscription] := discover: Type
  318. def discover: T {
  319. def [fut,res] := FuturesModule.makeFuture();
  320. def sub := when: T discovered: { |t|
  321. res.resolve(t);
  322. };
  323. [fut,sub];
  324. };
  325. // def [tFuture, discoverySubscription] := discover: Type where: { |t| filter(t) }
  326. def discover: T where: filter {
  327. def [arFuture, arResolver] := FuturesModule.makeFuture();
  328. def sub := DiscoveryModule.whenever: T discovered: { |remoteRef|
  329. def props := isolate: { nil };
  330. if: (is: remoteRef taggedAs: PropertyObject) then: {
  331. remoteRef := remoteRef.service;
  332. props := remoteRef.properties;
  333. };
  334. if: (filter(props)) then: {
  335. sub.cancel();
  336. arResolver.resolve(remoteRef)
  337. };
  338. };
  339. [arFuture, sub];
  340. };
  341. def ambient: T {
  342. makeAmbientReference(T, dfltFilter, dfltArity, dfltLifetime, dfltReply);
  343. };
  344. def ambient: T withArity: A {
  345. makeAmbientReference(T, dfltFilter, A, dfltLifetime, dfltReply);
  346. };
  347. def ambient: T withLifetime: L {
  348. makeAmbientReference(T, dfltFilter, dfltArity, L, dfltReply);
  349. };
  350. def ambient: T withArity: A withLifetime: L {
  351. makeAmbientReference(T, dfltFilter, A, L, dfltReply);
  352. };
  353. def ambient: T withReply: R {
  354. makeAmbientReference(T, dfltFilter, dfltArity, dfltLifetime, R);
  355. };
  356. def ambient: T withArity: A withReply: R {
  357. makeAmbientReference(T, dfltFilter, A, dfltLifetime, R);
  358. };
  359. def ambient: T withLifetime: L withReply: R {
  360. makeAmbientReference(T, dfltFilter, dfltArity, L, R);
  361. };
  362. def ambient: T withArity: A withLifetime: L withReply: R {
  363. makeAmbientReference(T, dfltFilter, A, L, R);
  364. };
  365. def ambient: T where: filter {
  366. makeAmbientReference(T, filter, dfltArity, dfltLifetime, dfltReply);
  367. };
  368. def ambient: T where: filter withArity: A {
  369. makeAmbientReference(T, filter, A, dfltLifetime, dfltReply);
  370. };
  371. def ambient: T where: filter withLifetime: L {
  372. makeAmbientReference(T, filter, dfltArity, L, dfltReply);
  373. };
  374. def ambient: T where: filter withReply: R {
  375. makeAmbientReference(T, filter, dfltArity, dfltLifetime, R);
  376. };
  377. def ambient: T where: filter withArity: A withLifetime: L withReply: R {
  378. makeAmbientReference(T, filter, A, L, R);
  379. };
  380. def snapshot: ref {
  381. ref<-makeSnapshot()@[FuturesModule.FutureMessage,MetaMessage];
  382. };
  383. def snapshot: ref after: period {
  384. def [f,r] := FuturesModule.makeFuture();
  385. when: period elapsed: {
  386. r.resolve(snapshot: ref);
  387. };
  388. f;
  389. };
  390. def AmbientRefsTest() {
  391. extend: /.at.unit.test.UnitTest.new(ARImplModule.implname + " AmbientRefTest") with: {
  392. deftype rARFooTest;
  393. deftype rARBarTest;
  394. deftype ARAsyncReplyServiceTestT;
  395. deftype ARNonExistentTestT;
  396. deftype ARManyServiceTestT;
  397. def providerHost := actor: { |ARImplModuleCapsule|
  398. deftype rARFooTest;
  399. def ARModule := /.at.lang.ambientrefs(ARImplModuleCapsule);
  400. def service := object: {
  401. def testMethod(x) { x+1 };
  402. };
  403. ARModule.export: service as: rARFooTest;
  404. deftype rARBarTest;
  405. def service2 := object: {
  406. def testContent(x) { x*x };
  407. };
  408. def y := 42;
  409. ARModule.export: service2 as: rARBarTest with: { |y| def z := 5 };
  410. deftype ARAsyncReplyServiceTestT;
  411. def service3 := object: {
  412. def replyTo(f) { f<-reply(42) };
  413. };
  414. ARModule.export: service3 as: ARAsyncReplyServiceTestT;
  415. deftype ARManyServiceTestT;
  416. def service4 := object: {
  417. def serve(request) { request + 1 };
  418. };
  419. ARModule.export: service4 as: ARManyServiceTestT;
  420. };
  421. def providerHost2 := actor: { |ARImplModuleCapsule|
  422. deftype ARManyServiceTestT;
  423. /.at.lang.ambientrefs(ARImplModuleCapsule).export: (object: {
  424. def serve(request) { request + 2 };
  425. }) as: ARManyServiceTestT;
  426. };
  427. def a := ambient: rARFooTest;
  428. def asyncReply := ambient: ARAsyncReplyServiceTestT;
  429. def omni := ambient: ARManyServiceTestT;
  430. // ONE, SUSTAIN, REPLY
  431. def testAsyncOneSustainFuture() {
  432. def handle := a<-testMethod(5)@[One,Sustain,Reply];
  433. FuturesModule.when: handle.future becomes: { |v|
  434. self.assertEquals(6,v);
  435. };
  436. };
  437. // ONE, TRANSIENT, REPLY
  438. def testAsyncOneTransientFuture() {
  439. def handle := a<-testMethod(5)@[One,Transient(seconds(14)),Reply];
  440. FuturesModule.when: handle.future becomes: { |v|
  441. self.assertEquals(6,v);
  442. } catch: { |e|
  443. self.fail("subject should have been found");
  444. };
  445. };
  446. // ONE, EXPIRES
  447. def testAsyncOneExpirable() {
  448. def blackHole := ambient: ARNonExistentTestT;
  449. def handle := blackHole<-testMethod(5)@[One,Expires(seconds(8))];
  450. FuturesModule.when: handle.future becomes: { |v|
  451. self.fail("method should have never been processed");
  452. } catch: { |e|
  453. self.assertTrue(is: e taggedAs: FuturesModule.TimeoutException);
  454. };
  455. };
  456. // ONE, INSTANT, ONEWAY
  457. def testOneInstantOneway() {
  458. def handle := a<-testMethod(5)@[One,Instant,Oneway];
  459. self.assertEquals(nil, handle.future);
  460. };
  461. // ONE, SUSTAIN, ONEWAY
  462. def testOneSustainOneway() {
  463. def handle := a<-testMethod(5)@[One,Sustain,Oneway];
  464. self.assertEquals(nil, handle.future);
  465. };
  466. // ONE, INSTANT, ONEWAY
  467. def testAsyncOneInstantOnewayNotDropped() {
  468. FuturesModule.future: { |return|
  469. // FIXME: currently, we're just waiting long enough and hoping
  470. // that the actor is up in time, but this is otherwise a very
  471. // unsafe unit test! (semantically the message may get lost)
  472. when: seconds(10) elapsed: {
  473. def handle := asyncReply<-replyTo(object: {
  474. def reply(x) {
  475. return(x);
  476. }
  477. })@[One,Instant,Oneway];
  478. self.assertEquals(nil, handle.future);
  479. }
  480. }
  481. };
  482. // ALL, TRANSIENT, DUE
  483. def testAsyncAllTransientDue() {
  484. def handle := omni<-serve(0)@[All,Transient(seconds(10)),Due(seconds(6))];
  485. MFuturesModule.whenAll: handle.future resolved: { |vals|
  486. self.assertEquals(2, vals.length);
  487. self.assertTrue(vals.contains(1));
  488. self.assertTrue(vals.contains(2));
  489. } ruined: { |excs|
  490. self.fail("unexpected exceptions: " + excs.map: { |exc| exc.message });
  491. };
  492. };
  493. // ALL, TRANSIENT, REPLY
  494. def testAsyncAllTransientFuture() {
  495. FuturesModule.future: { |reply|
  496. def count := 0;
  497. def handle := omni<-serve(0)@[All,Transient(seconds(15)),Reply];
  498. MFuturesModule.whenEach: handle.future becomes: { |val|
  499. self.assertTrue((val == 1).or: { val == 2 });
  500. count := count + 1;
  501. if: (count == 2) then: {
  502. reply(true);
  503. };
  504. };
  505. };
  506. };
  507. // ALL, EXPIRES
  508. def testAsyncAllExpires() {
  509. def handle := omni<-serve(0)@[All,Expires(seconds(12))];
  510. MFuturesModule.whenAll: handle.future resolved: { |vals|
  511. self.assertEquals(2, vals.length);
  512. self.assertTrue(vals.contains(1));
  513. self.assertTrue(vals.contains(2));
  514. } ruined: { |excs|
  515. self.fail("unexpected exceptions: " + excs.map: { |exc| exc.message });
  516. };
  517. };
  518. // ALL, SUSTAIN, ONEWAY
  519. def testAsyncAllSustainOneway() {
  520. FuturesModule.future: { |return|
  521. def handle := asyncReply<-replyTo(object: {
  522. def reply(x) {
  523. return(x);
  524. handle.cancel(); // stop sending the message
  525. }
  526. })@[All,Sustain,Oneway];
  527. self.assertEquals(nil, handle.future);
  528. }
  529. };
  530. def testAsyncMatchingWhere() {
  531. def a := ambient: rARBarTest where: (script: { |props| props.y > 40 } carrying: []);
  532. def handle := a<-testContent(5)@[One,Sustain,Reply];
  533. FuturesModule.when: handle.future becomes: { |v|
  534. self.assertEquals(25,v);
  535. } catch: { |e| raise: e };
  536. };
  537. def testAsyncNonMatchingWhere() {
  538. def a := ambient: rARBarTest where: (script: { |props| props.z == 6 } carrying: []);
  539. def handle := a<-testContent(5)@[One,Expires(seconds(10)),Reply];
  540. FuturesModule.when: handle.future becomes: { |v|
  541. self.fail("discovered a non-matching ambient reference");
  542. } catch: { |e|
  543. self.assertTrue(is: e taggedAs: FuturesModule.TimeoutException);
  544. };
  545. };
  546. def testAsyncParamPassing() {
  547. def act := actor: { def rcv(ar) { ar } };
  548. FuturesModule.when: act<-rcv(a)@FuturesModule.FutureMessage becomes: { |newAR|
  549. // after this, there will be 2 copies of a, one created upon parameter passing
  550. // and one created upon method return
  551. self.assertTrue(is: newAR taggedAs: AmbientReference);
  552. // self.assertFalse(is: newAR taggedAs: /.at.types.FarReference);
  553. self.assertFalse(a == newAR);
  554. };
  555. };
  556. // this unit test implements the simple voting example between proximate
  557. // players in a multiplayer game
  558. def testAsyncVoting() {
  559. deftype VotePlayerTest;
  560. def spawnPlayers(max) {
  561. 1.to: max+1 do: { |i|
  562. actor: { |i,ARImplModuleCapsule|
  563. deftype VotePlayerTest;
  564. /.at.lang.ambientrefs(ARImplModuleCapsule).export: (object: {
  565. def askToVote(poll) { i }
  566. }) as: VotePlayerTest with: {
  567. def team := "blue";
  568. };
  569. }
  570. }
  571. };
  572. def MAX := 5;
  573. spawnPlayers(MAX);
  574. def maxVoteTime := seconds(15);
  575. def proximateTeamPlayers := ambient: VotePlayerTest
  576. where: (script: { |p| p.team == "blue" } carrying: []);
  577. def handle := proximateTeamPlayers<-askToVote("poll")@[All,Expires(maxVoteTime)];
  578. MFuturesModule.whenAll: handle.future resolved: { |votes|
  579. //(handle.future<-sender())<-voteResult(votes)@Oneway;
  580. self.assertEquals(MAX, votes.length);
  581. 1.to: MAX+1 do: { |i|
  582. self.assertTrue(votes.contains(i));
  583. }
  584. } ruined: { |excs|
  585. self.fail("unexpected exceptions: " + excs.map: { |exc| exc.message });
  586. };
  587. };
  588. def protocolExporterPeer := actor: { |ARImplModuleCapsule|
  589. import /.at.lang.ambientrefs(ARImplModuleCapsule);
  590. export: (object: {
  591. def protocolAmbientRefTestMethod() { `ok }
  592. }) with: {
  593. def x := 42;
  594. };
  595. };
  596. def protocolAmbientRef := ambient: (ProtocolM.protocol: {
  597. def protocolAmbientRefTestMethod()
  598. });
  599. def testAsyncProtocolAmbientRef() {
  600. def handle := protocolAmbientRef<-protocolAmbientRefTestMethod()@[One,Sustain,Reply];
  601. FuturesModule.when: handle.future becomes: { |val|
  602. self.assertEquals(`ok, val);
  603. }
  604. };
  605. /* FIXME: extensional impl currently does not yet support exporting via protocol + attributes
  606. def protocolWhereAmbientRef := ambient: (ProtocolM.protocol: {
  607. def protocolAmbientRefTestMethod()
  608. }) where: (script: { |props| props.x > 10 } carrying: []);
  609. def testAsyncProtocolsAndWhere() {
  610. def handle := protocolWhereAmbientRef<-protocolAmbientRefTestMethod()@[One,Sustain,Reply];
  611. FuturesModule.when: handle.future becomes: { |val|
  612. self.assertEquals(`ok, val);
  613. }
  614. };*/
  615. def testAsyncSnapshot() {
  616. FuturesModule.when: (snapshot: omni after: seconds(10)) becomes: { |snapshot|
  617. self.assertEquals(2, snapshot.length);
  618. }
  619. };
  620. // ONE, SUSTAIN [as default], REPLY [as default]
  621. def testAsyncOneDefaultSustainDefaultFuture() {
  622. def defaultSustainFuture := ambient: rARFooTest withLifetime: Sustain withReply: Reply;
  623. def handle := defaultSustainFuture<-testMethod(5)@One;
  624. FuturesModule.when: handle.future becomes: { |v|
  625. self.assertEquals(6,v);
  626. };
  627. };
  628. };
  629. }; // end AmbientRefsTest
  630. }; // end anonymous module object
  631. }; // end AmbientRefsModule function