/* * Copyright (c) 2004-2005 Ricardo Fernández Pascual r.fernandez at ditec.um.es * Copyright (c) 2003-2008 The University of Wroclaw. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the University may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Threading; using Nemerle.Collections; using Nemerle.Macros; using Nemerle.Utility; using Nemerle.Compiler; using PT = Nemerle.Compiler.Parsetree; using TT = Nemerle.Compiler.Typedtree; namespace Nemerle.Concurrency { #region Helper module of Nemerle.Concurrency implementation module Helper { public MakeAsync (expr : PT.PExpr) : PT.PExpr { Util.locate (expr.Location, <[ def threadBody () { $expr }; def thread = Thread (ThreadStart (threadBody)); thread.Start (); ]>) } AddInit (t : TypeBuilder, is_static : bool, init : PT.PExpr) : void { AddInit (t, is_static, init, false) } AddInit (t : TypeBuilder, is_static : bool, init : PT.PExpr, after : bool) : void { def static_attr = if (is_static) BindingFlags.Static else BindingFlags.Instance; def mems = t.GetConstructors (static_attr %| BindingFlags.Public %| BindingFlags.NonPublic %| BindingFlags.DeclaredOnly); // given existing constructor, insert call to base constructor // at its beginning def inject (ctor) { def ctor = ctor :> MethodBuilder; def bd = ctor.Body; def newBody = Util.locate (ctor.Body.Location, if (after) <[ $bd; $init ]> else match (bd) { | <[ {.. $(e :: rest) } ]> => match (e) { | <[ base (..$_) ]> => <[ $e; $init; {.. $rest } ]> | <[ this (..$_) ]> => bd | _ => <[ $init; $bd ]> } | _ => <[ $init; $bd ]> }); ctor.Body = newBody; } match (mems) { | [] => if (is_static) t.Define (<[ decl: static public this () { $init } ]>) else t.Define (<[ decl: public this () { $init } ]>) | _ => List.Iter (mems, inject) } } public CreateChordCommonMembers (tb : TypeBuilder) : void { def is_in_current (mems) { | [x : IMember] => x.DeclaringType.Equals (tb) | [] => false | _ => assert (false) } unless (is_in_current (tb.LookupMember ("__Chord_Mask"))) { tb.Define (<[ decl: mutable __Chord_Mask : BitMask = BitMask (); // it must be mutable, otherwise on every use .NET creates just its copy and does not // modify it in place ]>); } unless (is_in_current (tb.LookupMember ("__Chord_Lock"))) { tb.Define ( <[ decl: __Chord_Lock : object; ]> ); AddInit (tb, false, <[ this.__Chord_Lock = object (); ]>) } unless (is_in_current (tb.LookupMember ("__Chord_Scan"))) { tb.Define ( <[ decl: __Chord_Scan () : void { } ]> ) } } AddChordScanCase (tb : TypeBuilder, mask : PT.Name, queue : PT.Name) : void { match (tb.LookupMember ("__Chord_Scan")) { | [m] => def m = (m :> MethodBuilder); def newBody = Util.locate (m.Body.Location, <[ if (this.__Chord_Mask.Match ($(mask : name))) { this.$(queue : name).Wakeup () } else { $(m.Body) } ]>); m.Body = newBody; | _ => assert (false) } } MaxMethodValueCounter : Hashtable [TypeBuilder, int];// = Hashtable (); this () { MaxMethodValueCounter = Hashtable () } public CreateChordMaskMethodValue (tb : TypeBuilder, m : MethodBuilder) : uint { def i = match (MaxMethodValueCounter.Get (tb)) { | Some (i) => i | None => 0 } MaxMethodValueCounter.Set (tb, i + 1); def value = 1U << i; def name = "__Chord_MaskMethodValue_" + m.Name; def symbol = Macros.UseSiteSymbol (name); tb.Define ( <[ decl: public static $(symbol : name) : uint /*= $(value : uint)*/; ]> ); AddInit (tb, true, <[ $(symbol : name) = $(value : uint); ]>); value } public CreateChordMaskValue (tb : TypeBuilder, m : MethodBuilder, members : list [PT.PExpr]) : PT.Name * PT.Name { def body_index = Util.tmpname (m.Name); def partialvalue = List.FoldLeft (members, <[ (0U : uint) ]>, fun (i, acc) { match (i) { | PT.PExpr.Ref (name) => def n = "__Chord_MaskMethodValue_" + name.Id; <[ $(acc) | $(n : usesite) ]> | _ => Message.FatalError ("wrong chord member"); } }); def partialname = "__Chord_MaskPartialValue_" + body_index; def partialsymbol = Macros.UseSiteSymbol (partialname); def ourMethodName = "__Chord_MaskMethodValue_" + m.Name; def ourMethodNameSymbol = Macros.UseSiteSymbol (ourMethodName); def value = <[ $(ourMethodNameSymbol : name) %| $(partialvalue) ]>; def name = "__Chord_MaskValue_" + body_index; def symbol = Macros.UseSiteSymbol (name); tb.Define ( <[ decl: static $(partialsymbol : name) : uint; ]>); tb.Define ( <[ decl: static $(symbol : name) : uint; ]>); AddInit (tb, true, <[ $(partialsymbol : name) = $(partialvalue); $(symbol : name) = $(value); ]>, true); (partialsymbol, symbol) } public CreateChordMethodQueue (tb : TypeBuilder, m : MethodBuilder) : PT.Name { def name = "__Chord_MethodQueue_" + m.Name; def symbol = Macros.UseSiteSymbol (name); def paramsTypes = match (m.GetMemType ()) { | MType.Fun (f, _) => f.Fix () } match (paramsTypes) { | MType.Void => tb.Define ( <[ decl: $(symbol : name) : DummyQueue; ]>); AddInit (tb, false, <[ this.$(symbol : name) = DummyQueue (); ]>) | _ => tb.Define ( <[ decl: $(symbol : name) : Queue [ $(paramsTypes : typed) ]; ]>); AddInit (tb, false, <[ this.$(symbol : name) = Queue (); ]>) } symbol } CreateChordThreadQueue (tb : TypeBuilder, m : MethodBuilder) : PT.Name { def name = "__Chord_ThreadQueue_" + m.Name; def symbol = Macros.UseSiteSymbol (name); tb.Define ( <[ decl: $(symbol : name) : ThreadQueue; ]> ); AddInit (tb, false, <[ this.$(symbol : name) = ThreadQueue (); ]>); symbol } public chord' (tb : TypeBuilder, m : MethodBuilder, chords : PT.PExpr) : void { CreateChordCommonMembers (tb); def qsymb = CreateChordThreadQueue (tb, m); def methodValue = CreateChordMaskMethodValue (tb, m); def chords = match (chords) { | <[ match ($_) { ..$cases } ]> => cases | _ => Message.FatalError (chords.Location, "wrong chord syntax") } def iterChords (chords : list [PT.MatchCase], acc) { match (chords) { | case :: rest => def members = List.Head (case.patterns); def body = case.body; def innerBody = <[ this.__Chord_Scan (); Monitor.Exit (this.__Chord_Lock); $(body) ]>; def unLift (e) { | <[ [..$result] ]> => result | <[ $result ]> => [result] } def members = unLift (members); def (partialmask, mask) = CreateChordMaskValue (tb, m, members); AddChordScanCase (tb, mask, qsymb); def execBody = List.FoldLeft (members, innerBody, fun (i, acc) { match (i) { | PT.PExpr.Ref (name) => def qn = "__Chord_MethodQueue_" + name.Id; def qnsymbol = Macros.UseSiteSymbol (qn); def member = match (tb.LookupMember (name.Id)) { | [m] => (m :> MethodBuilder) | _ => Message.FatalError ("wrong chord member " + name.Id); }; def cmmvn = "__Chord_MaskMethodValue_" + name.Id; def acc = <[ when (this.$(qnsymbol : name).IsEmpty) { this.__Chord_Mask.Clear ($(cmmvn : usesite)) } $acc ]>; match (member.GetParameters ()) { | [] => <[ this.$(qnsymbol : name).Take (); $acc ]> | [p] => <[ def $(p.name : usesite) = this.$(qnsymbol : name).Take (); $acc ]> | _ => def paramNames = List.FoldRight (member.GetParameters (), [], fun (p : TT.Fun_parm, acc) { <[ $(p.name : usesite) ]> :: acc }); <[ def (..$paramNames) = this.$(qnsymbol : name).Take (); $acc ]> } | _ => Message.FatalError (i.Location, "wrong chord member"); } }); iterChords (rest, <[ if (this.__Chord_Mask.Match ($(partialmask : name))) { $execBody; } else { $acc } ]>) | [] => acc } } def now = iterChords (chords, <[ this.__Chord_Mask.Set ($(methodValue : uint)); later () ]>); def newBody = Util.locate (m.Body.Location, <[ def later () { this.$(qsymb : name).Yield (this.__Chord_Lock); when (this.$(qsymb : name).Empty) { this.__Chord_Mask.Clear ($(methodValue : uint)) } now () } and now () { $now } Monitor.Enter (this.__Chord_Lock); if (this.__Chord_Mask.Match ($(methodValue : uint))) { later () } else { now () } ]>); m.Body = newBody; } } #endregion Helper module of Nemerle.Concurrency implementation /// -------------------- Macros of Nemerle.Concurrency namespace /* Executes an expresion asynchronously */ macro @async (expr) syntax ("async", expr) { Helper.MakeAsync (<[ ($expr : void) ]>); } /* Executes the body of the method always asynchronously */ [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Method, Inherited = true)] macro Async (_ : TypeBuilder, m : ParsedMethod) syntax ("async") { m.Body = Helper.MakeAsync (m.Body) } [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, Nemerle.MacroTargets.Method, Inherited = true)] macro ChordMember (_ : TypeBuilder, m : ParsedMethod) { // we temporarily set body of method, so methods with abstract like // body could be allowed def newBody = Util.locate(m.Body.Location, <[ () ]>); m.Body = newBody; } [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers, Nemerle.MacroTargets.Method, Inherited = true)] macro ChordMember (tb : TypeBuilder, m : MethodBuilder) { def loc = m.Body.Location; Helper.CreateChordCommonMembers (tb); def qsymb = Helper.CreateChordMethodQueue (tb, m); def methodValue = Helper.CreateChordMaskMethodValue (tb, m); def paramslist = List.FoldRight (m.GetParameters (), [], fun (p : TT.Fun_parm, acc) { <[ $(p.name : usesite) ]> :: acc }); match (paramslist) { | [] => m.Body = <[ $(m.Body); this.$(qsymb : name).Add () ]> | [p] => m.Body = <[ $(m.Body); this.$(qsymb : name).Add ($(p)) ]> | _ => def tuple = <[ (.. $paramslist) ]>; m.Body = <[ $(m.Body); this.$(qsymb : name).Add ($(tuple)) ]> } def newBody = Util.locate(loc, <[ lock (this.__Chord_Lock) { $(m.Body); unless (this.__Chord_Mask.Match ($(methodValue : uint))) { this.__Chord_Mask.Set ($(methodValue : uint)); this.__Chord_Scan () } } ]>); m.Body = newBody; } // FIXME: if one of the members is declared after the Chord body, things can go wrong. [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers, Nemerle.MacroTargets.Method, Inherited = true)] macro Chord (tb : TypeBuilder, m : MethodBuilder) syntax ("chord") { Helper.chord' (tb, m, m.Body) } [Nemerle.MacroUsage (Nemerle.MacroPhase.WithTypedMembers, Nemerle.MacroTargets.Method, Inherited = true)] macro AsyncChord (tb : TypeBuilder, m : MethodBuilder) { Helper.chord' (tb, m, m.Body); m.Body = Helper.MakeAsync (m.Body) } }