Please disable Adblockers and enable JavaScript for domain CEWebS.cs.univie.ac.at! We have NO ADS, but they may interfere with some of our course material.

Name: lib/weel.rb 
1:
# encoding: utf-8
2:
#
3:
# This file is part of WEEL.
4:
#
5:
# WEEL is free software: you can redistribute it and/or modify it under the terms
6:
# of the GNU General Public License as published by the Free Software Foundation,
7:
# either version 3 of the License, or (at your option) any later version.
8:
#
9:
# WEEL is distributed in the hope that it will be useful, but WITHOUT ANY
10:
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11:
# PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12:
#
13:
# You should have received a copy of the GNU General Public License along with
14:
# WEEL (file COPYING in the main directory).  If not, see
15:
# <http://www.gnu.org/licenses/>.
16:
 
17:
require 'thread'
18:
 
19:
# OMG!111! deep cloning for ReadHashes
20:
class Object # {{{
21:
  def deep_clone
22:
    return @deep_cloning_obj if @deep_cloning
23:
    @deep_cloning_obj = clone
24:
    @deep_cloning_obj.instance_variables.each do |var|
25:
      val = @deep_cloning_obj.instance_variable_get(var)
26:
      begin
27:
        @deep_cloning = true
28:
        val = val.deep_clone
29:
      rescue TypeError
30:
        next
31:
      ensure
32:
        @deep_cloning = false
33:
      end
34:
      @deep_cloning_obj.instance_variable_set(var, val)
35:
    end
36:
    deep_cloning_obj = @deep_cloning_obj
37:
    @deep_cloning_obj = nil
38:
    deep_cloning_obj
39:
  end
40:
end #}}}
41:
 
42:
class WEEL
43:
  def initialize(*args)# {{{
44:
    @dslr = DSLRealization.new
45:
    @dslr.__weel_handlerwrapper_args = args
46:
 
47:
    initialize_search if methods.include?(:initialize_search)
48:
    initialize_data if methods.include?(:initialize_data)
49:
    initialize_endpoints if methods.include?(:initialize_endpoints)
50:
    initialize_handlerwrapper if methods.include?(:initialize_handlerwrapper)
51:
    initialize_control if methods.include?(:initialize_control)
52:
  end # }}}
53:
 
54:
  module Signal # {{{
55:
    class Skip < Exception; end
56:
    class SkipManipulate < Exception; end
57:
    class StopSkipManipulate < Exception; end
58:
    class Stop < Exception; end
59:
    class Proceed < Exception; end
60:
    class NoLongerNecessary < Exception; end
61:
    class Again < Exception; end
62:
    class Error < Exception; end
63:
  end # }}}
64:
 
65:
  class ReadStructure # {{{
66:
    def initialize(data,endpoints)
67:
      @__weel_data = data
68:
      @__weel_endpoints = endpoints
69:
      @changed_data = []
70:
      @changed_endpoints = []
71:
    end
72:
 
73:
    def data
74:
      ReadHash.new(@__weel_data)
75:
    end
76:
    def endpoints
77:
      ReadHash.new(@__weel_endpoints)
78:
    end
79:
  end # }}}
80:
  class ManipulateStructure # {{{
81:
    def initialize(data,endpoints,status)
82:
      @__weel_data = data
83:
      @__weel_data_orig = @__weel_data.transform_values{|val| Marshal.dump(val) }
84:
      @__weel_endpoints = endpoints
85:
      @__weel_endpoints_orig = @__weel_endpoints.transform_values{|val| Marshal.dump(val) }
86:
      @__weel_status = status
87:
      @changed_status = "#{status.id}-#{status.message}"
88:
      @changed_data = []
89:
      @touched_data = []
90:
      @changed_endpoints = []
91:
      @touched_endpoints = []
92:
    end
93:
 
94:
    def changed_data
95:
      @touched_data.each do |e|
96:
        if Marshal.dump(@__weel_data[e]) != @__weel_data_orig[e]
97:
          @changed_data << e
98:
        end
99:
      end
100:
      @changed_data
101:
    end
102:
    def changed_endpoints
103:
      @changed_endpoints
104:
    end
105:
 
106:
    def original_data
107:
      @__weel_data_orig.transform_values{|val| Marshal.load(val) }
108:
    end
109:
 
110:
    def original_endpoints
111:
      @__weel_endpoints_orig.transform_values{|val| Marshal.load(val) }
112:
    end
113:
 
114:
    def changed_status
115:
      @changed_status != "#{status.id}-#{status.message}"
116:
    end
117:
 
118:
    def data
119:
      ManipulateHash.new(@__weel_data,@touched_data,@changed_data)
120:
    end
121:
    def endpoints
122:
      ManipulateHash.new(@__weel_endpoints,@touched_endpoints,@changed_endpoints)
123:
    end
124:
    def status
125:
      @__weel_status
126:
    end
127:
  end # }}}
128:
  class ManipulateHash # {{{
129:
    attr_reader :__weel_touched, :__weel_changed
130:
 
131:
    def initialize(values,touched,changed)
132:
      @__weel_values = values
133:
      @__weel_touched = touched
134:
      @__weel_changed = changed
135:
    end
136:
 
137:
    def delete(value)
138:
      if @__weel_values.key?(value)
139:
        @__weel_changed << value
140:
        @__weel_values.delete(value)
141:
      end
142:
    end
143:
 
144:
    def clear
145:
      @__weel_changed += @__weel_values.keys
146:
      @__weel_values.clear
147:
    end
148:
 
149:
    def method_missing(name,*args)
150:
      if args.empty? && @__weel_values.key?(name)
151:
        @__weel_touched << name
152:
        @__weel_values[name]
153:
      elsif name.to_s[-1..-1] == "=" && args.length == 1
154:
        temp = name.to_s[0..-2]
155:
        @__weel_changed << temp.to_sym
156:
        @__weel_values[temp.to_sym] = args[0]
157:
      elsif name.to_s == "[]=" && args.length == 2
158:
        @__weel_changed << args[0]
159:
        @__weel_values[args[0]] = args[1]
160:
      elsif name.to_s == "[]" && args.length == 1
161:
        @__weel_touched << args[0]
162:
        @__weel_values[args[0]]
163:
      else
164:
        nil
165:
      end
166:
    end
167:
  end # }}}
168:
 
169:
  class Status # {{{
170:
    def initialize(id,message)
171:
      @id      = id
172:
      @message = message
173:
      @nudge   = Queue.new
174:
    end
175:
    def update(id,message)
176:
      @id      = id
177:
      @message = message
178:
    end
179:
    def nudge!
180:
      @nudge.clear
181:
      @nudge.push(nil)
182:
    end
183:
    def wait_until_nudged!
184:
      @nudge.pop
185:
    end
186:
    attr_reader :id, :message
187:
  end #}}}
188:
 
189:
  class ReadHash # {{{
190:
    def initialize(values,sim=false)
191:
      @__weel_values = values
192:
      @__weel_sim = sim
193:
    end
194:
 
195:
    def to_json(*args)
196:
      @__weel_values.to_json(*args)
197:
    end
198:
 
199:
    def method_missing(name,*args)
200:
      if args.empty? && @__weel_values.key?(name)
201:
        if @__weel_sim
202:
          "➤#{name}"
203:
        else
204:
          @__weel_values[name]
205:
        end
206:
        #TODO dont let user change stuff e.g. if return value is an array (deep clone and/or deep freeze it?)
207:
      else
208:
        nil
209:
      end
210:
    end
211:
  end # }}}
212:
 
213:
  class HandlerWrapperBase # {{{
214:
    def self::inform_state_change(arguments,newstate); end
215:
    def self::inform_syntax_error(arguments,err,code); end
216:
    def self::inform_handlerwrapper_error(arguments,err); end
217:
    def self::inform_position_change(arguments,ipc); end
218:
 
219:
    def initialize(arguments,endpoint=nil,position=nil,continue=nil); end
220:
 
221:
    def activity_handle(passthrough, parameters); end
222:
    def activity_manipulate_handle(parameters); end
223:
 
224:
    def activity_result_value; end
225:
 
226:
    def activity_stop; end
227:
    def activity_passthrough_value; end
228:
 
229:
    def activity_no_longer_necessary; end
230:
 
231:
    def inform_activity_done; end
232:
    def inform_activity_manipulate; end
233:
    def inform_activity_failed(err); end
234:
    def inform_manipulate_change(status,changed_data,changed_endpoints,data,endpoints); end
235:
 
236:
    def vote_sync_before(parameters=nil); true; end
237:
    def vote_sync_after; true; end
238:
 
239:
    # type       => activity, loop, parallel, choice
240:
    # nesting    => none, start, end
241:
    # eid        => id's also for control structures
242:
    # parameters => stuff given to the control structure
243:
    def simulate(type,nesting,sequence,parent,parameters={}); end
244:
 
245:
    def callback(result=nil,options={}); end
246:
 
247:
    def test_condition(mr,code); mr.instance_eval(code); end
248:
    def manipulate(mr,code,result=nil); mr.instance_eval(code); end
249:
  end  # }}}
250:
 
251:
  class Position # {{{
252:
    attr_reader :position
253:
    attr_accessor :detail, :passthrough
254:
    def initialize(position, detail=:at, passthrough=nil) # :at or :after or :unmark
255:
      @position = position
256:
      @detail = detail
257:
      @passthrough = passthrough
258:
    end
259:
  end # }}}
260:
 
261:
   class Continue # {{{
262:
     def initialize
263:
       @q = Queue.new
264:
       @m = Mutex.new
265:
     end
266:
     def waiting?
267:
       @m.synchronize do
268:
         !@q.empty?
269:
       end
270:
     end
271:
     def continue(*args)
272:
       @q.push(args.length <= 1 ? args[0] : args)
273:
     end
274:
     def clear
275:
      @q.clear
276:
     end
277:
     def wait
278:
       @q.deq
279:
     end
280:
   end #}}}
281:
 
282:
  def self::search(weel_search)# {{{
283:
    define_method :initialize_search do
284:
      self.search weel_search
285:
    end
286:
  end # }}}
287:
  def self::endpoint(new_endpoints)# {{{
288:
    @@__weel_new_endpoints ||= {}
289:
    @@__weel_new_endpoints.merge! new_endpoints
290:
    remove_method :initialize_endpoints if method_defined? :initialize_endpoints
291:
    define_method :initialize_endpoints do
292:
      @@__weel_new_endpoints.each do |name,value|
293:
        @dslr.__weel_endpoints[name.to_s.to_sym] = value
294:
      end
295:
    end
296:
  end # }}}
297:
  def self::data(data_elements)# {{{
298:
    @@__weel_new_data_elements ||= {}
299:
    @@__weel_new_data_elements.merge! data_elements
300:
    define_method :initialize_data do
301:
      @@__weel_new_data_elements.each do |name,value|
302:
        @dslr.__weel_data[name.to_s.to_sym] = value
303:
      end
304:
    end
305:
  end # }}}
306:
  def self::handlerwrapper(aClassname, *args)# {{{
307:
    define_method :initialize_handlerwrapper do
308:
      self.handlerwrapper = aClassname
309:
      self.handlerwrapper_args = args unless args.empty?
310:
    end
311:
  end # }}}
312:
  def self::control(flow, &block)# {{{
313:
    @@__weel_control_block = block
314:
    define_method :initialize_control do
315:
      self.description = @@__weel_control_block
316:
    end
317:
  end #  }}}
318:
  def self::flow # {{{
319:
  end #}}}
320:
 
321:
  class DSLRealization # {{{
322:
    def  initialize #{{{
323:
      @__weel_search_positions = {}
324:
      @__weel_positions = Array.new
325:
      @__weel_main = nil
326:
      @__weel_data ||= Hash.new
327:
      @__weel_endpoints ||= Hash.new
328:
      @__weel_handlerwrapper = HandlerWrapperBase
329:
      @__weel_handlerwrapper_args = []
330:
      @__weel_state = :ready
331:
      @__weel_status = Status.new(0,"undefined")
332:
      @__weel_sim = -1
333:
    end #}}}
334:
    attr_accessor :__weel_search_positions, :__weel_positions, :__weel_main, :__weel_data, :__weel_endpoints, :__weel_handlerwrapper, :__weel_handlerwrapper_args
335:
    attr_reader :__weel_state, :__weel_status
336:
 
337:
    # DSL-Constructs for atomic calls to external services (calls) and pure context manipulations (manipulate).
338:
    # Calls can also manipulate context (after the invoking the external services)
339:
    # position: a unique identifier within the wf-description (may be used by the search to identify a starting point)
340:
    # endpoint: (only with :call) ep of the service
341:
    # parameters: (only with :call) service parameters
342:
    def call(position, endpoint, parameters: {}, finalize: nil, update: nil, &finalizeblk) #{{{
343:
      __weel_activity(position,:call,endpoint,parameters,finalize||finalizeblk,update)
344:
    end #}}}
345:
    # when two params, second param always script
346:
    # when block and two params, parameters stays
347:
    def manipulate(position, parameters=nil, script=nil, &scriptblk) #{{{
348:
      if scriptblk.nil? && script.nil? && !parameters.nil?
349:
        script, parameters = parameters, nil
350:
      end
351:
      __weel_activity(position,:manipulate,nil,parameters||{},script||scriptblk)
352:
    end #}}}
353:
 
354:
    # Parallel DSL-Construct
355:
    # Defines Workflow paths that can be executed parallel.
356:
    # May contain multiple branches (parallel_branch)
357:
    def parallel(type=nil)# {{{
358:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
359:
 
360:
      Thread.current[:branches] = []
361:
      Thread.current[:branch_finished_count] = 0
362:
      Thread.current[:branch_event] = Continue.new
363:
      Thread.current[:mutex] = Mutex.new
364:
 
365:
      hw, pos = __weel_sim_start(:parallel) if __weel_sim
366:
 
367:
      __weel_protect_yield(&Proc.new)
368:
 
369:
      Thread.current[:branch_wait_count] = (type.is_a?(Hash) && type.size == 1 && type[:wait] != nil && (type[:wait].is_a?(Integer) && type[:wait] > 0) ? type[:wait] : Thread.current[:branches].size)
370:
      1.upto Thread.current[:branches].size do
371:
        Thread.current[:branch_event].wait
372:
      end
373:
 
374:
      Thread.current[:branches].each do |thread|
375:
        # decide after executing block in parallel cause for coopis
376:
        # it goes out of search mode while dynamically counting branches
377:
        if Thread.current[:branch_search] == false
378:
          thread[:branch_search] = false
379:
        end
380:
        thread[:start_event].continue
381:
      end
382:
 
383:
      Thread.current[:branch_event].wait
384:
 
385:
      __weel_sim_stop(:parallel,hw,pos) if __weel_sim
386:
 
387:
      unless self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
388:
        # first set all to no_longer_neccessary
389:
        Thread.current[:branches].each do |thread|
390:
          if thread.alive?
391:
            thread[:nolongernecessary] = true
392:
            __weel_recursive_continue(thread)
393:
          end
394:
        end
395:
        # wait for all
396:
        Thread.current[:branches].each do |thread|
397:
          __weel_recursive_join(thread)
398:
        end
399:
      end
400:
    end # }}}
401:
 
402:
    # Defines a branch of a parallel-Construct
403:
    def parallel_branch(*vars)# {{{
404:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
405:
      branch_parent = Thread.current
406:
 
407:
      if __weel_sim
408:
        # catch the potential execution in loops inside a parallel
409:
        current_branch_sim_pos = branch_parent[:branch_sim_pos]
410:
      end
411:
 
412:
      Thread.current[:branches] << Thread.new(*vars) do |*local|
413:
        Thread.current.abort_on_exception = true
414:
        Thread.current[:branch_status] = false
415:
        Thread.current[:branch_parent] = branch_parent
416:
        Thread.current[:start_event] = Continue.new
417:
 
418:
        if __weel_sim
419:
          Thread.current[:branch_sim_pos] = @__weel_sim += 1
420:
        end
421:
 
422:
        # parallel_branch could be possibly around an alternative. Thus thread has to inherit the alternative_executed
423:
        # after branching, update it in the parent (TODO)
424:
        if branch_parent[:alternative_executed] && branch_parent[:alternative_executed].length > 0
425:
          Thread.current[:alternative_executed] = [branch_parent[:alternative_executed].last]
426:
          Thread.current[:alternative_mode] = [branch_parent[:alternative_mode].last]
427:
        end
428:
        branch_parent[:branch_event].continue
429:
        Thread.current[:start_event].wait
430:
 
431:
        if __weel_sim
432:
          handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
433:
          handlerwrapper.simulate(:parallel_branch,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos)
434:
        end
435:
 
436:
        __weel_protect_yield(*local, &Proc.new)
437:
 
438:
        __weel_sim_stop(:parallel_branch,handlerwrapper,current_branch_sim_pos) if __weel_sim
439:
 
440:
        branch_parent[:mutex].synchronize do
441:
          Thread.current[:branch_status] = true
442:
          branch_parent[:branch_finished_count] += 1
443:
          if branch_parent[:branch_finished_count] == branch_parent[:branch_wait_count] && self.__weel_state != :stopping && self.__weel_state != :finishing
444:
            branch_parent[:branch_event].continue
445:
          end
446:
        end
447:
        if self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
448:
          if Thread.current[:branch_position]
449:
            @__weel_positions.delete Thread.current[:branch_position]
450:
            begin
451:
              ipc = {}
452:
              ipc[:unmark] = [Thread.current[:branch_position].position]
453:
              @__weel_handlerwrapper::inform_position_change(@__weel_handlerwrapper_args,ipc)
454:
            end rescue nil
455:
            Thread.current[:branch_position] = nil
456:
          end
457:
        end
458:
      end
459:
    end # }}}
460:
 
461:
    # Choose DSL-Construct
462:
    # Defines a choice in the Workflow path.
463:
    # May contain multiple execution alternatives
464:
    def choose(mode=:inclusive) # {{{
465:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
466:
      Thread.current[:alternative_executed] ||= []
467:
      Thread.current[:alternative_mode] ||= []
468:
      Thread.current[:alternative_executed] << false
469:
      Thread.current[:alternative_mode] << mode
470:
      hw, pos = __weel_sim_start(:choose,:mode => Thread.current[:alternative_mode].last) if __weel_sim
471:
      __weel_protect_yield(&Proc.new)
472:
      __weel_sim_stop(:choose,hw,pos,:mode => Thread.current[:alternative_mode].last) if __weel_sim
473:
      Thread.current[:alternative_executed].pop
474:
      Thread.current[:alternative_mode].pop
475:
      nil
476:
    end # }}}
477:
 
478:
    # Defines a possible choice of a choose-Construct
479:
    # Block is executed if condition == true or
480:
    # searchmode is active (to find the starting position)
481:
    def alternative(condition,args={})# {{{
482:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
483:
      hw, pos = __weel_sim_start(:alternative,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
484:
      Thread.current[:mutex] ||= Mutex.new
485:
      Thread.current[:mutex].synchronize do
486:
        return if Thread.current[:alternative_mode][-1] == :exclusive && Thread.current[:alternative_executed][-1] == true
487:
        if (condition.is_a?(String) || condition.is_a?(Proc)) && !__weel_sim
488:
          condition = __weel_eval_condition(condition)
489:
        end
490:
        Thread.current[:alternative_executed][-1] = true if condition
491:
      end
492:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || condition
493:
      __weel_sim_stop(:alternative,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
494:
    end # }}}
495:
    def otherwise(args={}) # {{{
496:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
497:
      hw, pos = __weel_sim_start(:otherwise,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
498:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || !Thread.current[:alternative_executed].last
499:
      __weel_sim_stop(:otherwise,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
500:
    end # }}}
501:
 
502:
    # Defines a critical block (=Mutex)
503:
    def critical(id)# {{{
504:
      @__weel_critical ||= Mutex.new
505:
      semaphore = nil
506:
      @__weel_critical.synchronize do
507:
        @__weel_critical_sections ||= {}
508:
        semaphore = @__weel_critical_sections[id] ? @__weel_critical_sections[id] : Mutex.new
509:
        @__weel_critical_sections[id] = semaphore if id
510:
      end
511:
      semaphore.synchronize do
512:
        __weel_protect_yield(&Proc.new)
513:
      end
514:
    end # }}}
515:
 
516:
    # Defines a Cycle (loop/iteration)
517:
    def loop(condition,args={})# {{{
518:
      unless condition.is_a?(Array) && (condition[0].is_a?(Proc) || condition[0].is_a?(String)) && [:pre_test,:post_test].include?(condition[1]) && args.is_a?(Hash)
519:
        raise "condition must be called pre_test{} or post_test{}"
520:
      end
521:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
522:
      if __weel_is_in_search_mode
523:
        catch :escape do
524:
          __weel_protect_yield(&Proc.new)
525:
        end
526:
        if __weel_is_in_search_mode
527:
          return
528:
        else
529:
          ### in case it was a :post_test we wake inside the loop so we can check
530:
          ### condition first thing
531:
          condition[1] = :pre_test
532:
        end
533:
      end
534:
      if __weel_sim
535:
        cond = condition[0].is_a?(Proc) ? true : condition[0]
536:
        hw, pos = __weel_sim_start(:loop,args.merge(:testing=>condition[1],:condition=>cond))
537:
        catch :escape do
538:
          __weel_protect_yield(&Proc.new)
539:
        end
540:
        __weel_sim_stop(:loop,hw,pos,args.merge(:testing=>condition[1],:condition=>cond))
541:
        return
542:
      end
543:
      catch :escape do
544:
        case condition[1]
545:
          when :pre_test
546:
            while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
547:
              __weel_protect_yield(&Proc.new)
548:
            end
549:
          when :post_test
550:
            begin
551:
              __weel_protect_yield(&Proc.new)
552:
            end while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
553:
        end
554:
      end
555:
    end # }}}
556:
 
557:
    def test(code=nil,&blk)# {{{
558:
      code || blk
559:
    end # }}}
560:
    def pre_test(code=nil,&blk)# {{{
561:
      [code || blk, :pre_test]
562:
    end # }}}
563:
    def post_test(code=nil,&blk)# {{{
564:
      [code || blk, :post_test]
565:
    end # }}}
566:
 
567:
    def escape #{{{
568:
      return if __weel_is_in_search_mode
569:
      throw :escape
570:
    end #}}}
571:
    def terminate #{{{
572:
      return if __weel_is_in_search_mode
573:
      self.__weel_state = :finishing
574:
    end #}}}
575:
    def stop(position) #{{{
576:
      searchmode = __weel_is_in_search_mode(position)
577:
      return if searchmode
578:
      __weel_progress searchmode, position, true
579:
      self.__weel_state = :stopping
580:
    end #}}}
581:
 
582:
    def status # {{{
583:
      @__weel_status
584:
    end # }}}
585:
    def data # {{{
586:
      ReadHash.new(@__weel_data,__weel_sim)
587:
    end # }}}
588:
    def endpoints # {{{
589:
      ReadHash.new(@__weel_endpoints)
590:
    end # }}}
591:
 
592:
  private
593:
    def __weel_protect_yield(*local) #{{{
594:
      begin
595:
        yield(*local) if block_given?
596:
      rescue NameError => err # don't look into it, or it will explode
597:
        self.__weel_state = :stopping
598:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("protect_yield: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
599:
        nil
600:
      rescue => err
601:
        self.__weel_state = :stopping
602:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
603:
        nil
604:
      end
605:
    end #}}}
606:
 
607:
    def __weel_eval_condition(condition) #{{{
608:
      begin
609:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args unless condition.is_a?(Proc)
610:
        condition.is_a?(Proc) ? condition.call : handlerwrapper.test_condition(ReadStructure.new(@__weel_data,@__weel_endpoints),condition)
611:
      rescue NameError => err # don't look into it, or it will explode
612:
        # if you access $! here, BOOOM
613:
        self.__weel_state = :stopping
614:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("eval_condition: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
615:
        nil
616:
      rescue => err
617:
        self.__weel_state = :stopping
618:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
619:
        nil
620:
      end
621:
    end #}}}
622:
 
623:
    def __weel_progress(searchmode, position, skip=false) #{{{
624:
      ipc = {}
625:
      if searchmode == :after
626:
        wp = WEEL::Position.new(position, :after, nil)
627:
        ipc[:after] = [wp.position]
628:
      else
629:
        if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
630:
          @__weel_positions.delete Thread.current[:branch_parent][:branch_position]
631:
          ipc[:unmark] ||= []
632:
          ipc[:unmark] << Thread.current[:branch_parent][:branch_position].position rescue nil
633:
          Thread.current[:branch_parent][:branch_position] = nil
634:
        end
635:
        if Thread.current[:branch_position]
636:
          @__weel_positions.delete Thread.current[:branch_position]
637:
          ipc[:unmark] ||= []
638:
          ipc[:unmark] << Thread.current[:branch_position].position rescue nil
639:
        end
640:
        wp = WEEL::Position.new(position, skip ? :after : :at, nil)
641:
        ipc[skip ? :after : :at] = [wp.position]
642:
      end
643:
      @__weel_positions << wp
644:
      Thread.current[:branch_position] = wp
645:
 
646:
      @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, ipc
647:
      wp
648:
    end #}}}
649:
 
650:
    def __weel_activity(position, type, endpoints, parameters, finalize, update=nil)# {{{
651:
      position = __weel_position_test position
652:
      begin
653:
        searchmode = __weel_is_in_search_mode(position)
654:
        return if searchmode == true
655:
        return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
656:
 
657:
        Thread.current[:continue] = Continue.new
658:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args, endpoints.is_a?(Array) ? endpoints.map{ |ep| @__weel_endpoints[ep] }.compact : @__weel_endpoints[endpoints], position, Thread.current[:continue]
659:
 
660:
        if __weel_sim
661:
          handlerwrapper.simulate(:activity,:none,@__weel_sim += 1,Thread.current[:branch_sim_pos],:position => position,:parameters => parameters,:endpoints => endpoints,:type => type,:finalize => finalize.is_a?(String) ? finalize : nil)
662:
          return
663:
        end
664:
 
665:
        wp = __weel_progress searchmode, position
666:
 
667:
        # searchmode position is after, jump directly to vote_sync_after
668:
        raise Signal::Proceed if searchmode == :after
669:
 
670:
        case type
671:
          when :manipulate
672:
            raise Signal::Stop unless handlerwrapper.vote_sync_before
673:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
674:
 
675:
            if finalize.is_a?(Proc) || finalize.is_a?(String)
676:
              handlerwrapper.activity_manipulate_handle(parameters)
677:
              handlerwrapper.inform_activity_manipulate
678:
              if finalize.is_a?(Proc)
679:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
680:
                mr.instance_eval(&finalize)
681:
              elsif finalize.is_a?(String)
682:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
683:
                handlerwrapper.manipulate(mr,finalize)
684:
              end
685:
              handlerwrapper.inform_manipulate_change(
686:
                ((mr && mr.changed_status) ? @__weel_status : nil),
687:
                ((mr && mr.changed_data.any?) ? mr.changed_data.uniq : nil),
688:
                ((mr && mr.changed_endpoints.any?) ? mr.changed_endpoints.uniq : nil),
689:
                @__weel_data,
690:
                @__weel_endpoints
691:
              )
692:
              handlerwrapper.inform_activity_done
693:
              wp.detail = :after
694:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
695:
            end
696:
          when :call
697:
            params = { }
698:
            case parameters
699:
              when Hash
700:
                parameters.each do |k,p|
701:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
702:
                    params[k] = @__weel_data[p]
703:
                  else
704:
                    params[k] = p
705:
                  end
706:
                end
707:
              when Array
708:
                parameters.each_with_index do |p,i|
709:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
710:
                    params[p] = @__weel_data[p]
711:
                  else
712:
                    params[i] = p
713:
                  end
714:
                end
715:
              else
716:
                raise("invalid parameters")
717:
            end
718:
            raise Signal::Stop unless handlerwrapper.vote_sync_before(params)
719:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
720:
 
721:
            if @__weel_search_positions[position]
722:
              passthrough = @__weel_search_positions[position].passthrough
723:
              @__weel_search_positions[position].passthrough = nil
724:
            else
725:
              passthrough = nil
726:
            end
727:
 
728:
            handlerwrapper.activity_handle passthrough, params
729:
            wp.passthrough = handlerwrapper.activity_passthrough_value
730:
            begin
731:
              # with loop if catching Signal::Again
732:
              # handshake call and wait until it finished
733:
              waitingresult = nil
734:
              waitingresult = Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
735:
              raise waitingresult[1] if !waitingresult.nil? && waitingresult.is_a?(Array) && waitingresult.length == 2 && waitingresult[0] == WEEL::Signal::Error
736:
 
737:
              if Thread.current[:nolongernecessary]
738:
                handlerwrapper.activity_no_longer_necessary
739:
                raise Signal::NoLongerNecessary
740:
              end
741:
              if self.__weel_state == :stopping || self.__weel_state == :finishing
742:
                handlerwrapper.activity_stop
743:
                wp.passthrough = handlerwrapper.activity_passthrough_value
744:
                raise Signal::Proceed
745:
              end
746:
 
747:
              code = waitingresult == Signal::Again ? update : finalize
748:
              if code.is_a?(Proc) || code.is_a?(String)
749:
                handlerwrapper.inform_activity_manipulate
750:
                if code.is_a?(Proc)
751:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
752:
                  case code.arity
753:
                    when 1; mr.instance_exec(handlerwrapper.activity_result_value,&code)
754:
                    when 2; mr.instance_exec(handlerwrapper.activity_result_value,&code)
755:
                    else
756:
                      mr.instance_exec(&code)
757:
                  end
758:
                elsif code.is_a?(String)
759:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
760:
                  handlerwrapper.manipulate(mr,code,handlerwrapper.activity_result_value)
761:
                end
762:
                handlerwrapper.inform_manipulate_change(
763:
                  (mr.changed_status ? @__weel_status : nil),
764:
                  (mr.changed_data.any? ? mr.changed_data.uniq : nil),
765:
                  (mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil),
766:
                  @__weel_data,
767:
                  @__weel_endpoints
768:
                )
769:
              end
770:
            end while waitingresult == Signal::Again
771:
            if handlerwrapper.activity_passthrough_value.nil?
772:
              handlerwrapper.inform_activity_done
773:
              wp.detail = :after
774:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
775:
            end
776:
        end
777:
        raise Signal::Proceed
778:
      rescue Signal::SkipManipulate, Signal::Proceed
779:
        if self.__weel_state != :stopping && self.__weel_state != :finishing && !handlerwrapper.vote_sync_after
780:
          self.__weel_state = :stopping
781:
          wp.detail = :unmark
782:
        end
783:
      rescue Signal::NoLongerNecessary
784:
        @__weel_positions.delete wp
785:
        Thread.current[:branch_position] = nil
786:
        wp.detail = :unmark
787:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :unmark => [wp.position]
788:
      rescue Signal::StopSkipManipulate, Signal::Stop
789:
        self.__weel_state = :stopping
790:
      rescue Signal::Skip
791:
        nil
792:
      rescue SyntaxError => se
793:
        handlerwrapper.inform_activity_failed se
794:
        self.__weel_state = :stopping
795:
      rescue => err
796:
        handlerwrapper.inform_activity_failed err
797:
        self.__weel_state = :stopping
798:
      ensure
799:
        Thread.current[:continue].clear if Thread.current[:continue] && Thread.current[:continue].is_a?(Continue)
800:
      end
801:
    end # }}}
802:
 
803:
    def __weel_recursive_print(thread,indent='')# {{{
804:
      p "#{indent}#{thread}"
805:
      if thread[:branches]
806:
        thread[:branches].each do |b|
807:
          __weel_recursive_print(b,indent+'  ')
808:
        end
809:
      end
810:
    end  # }}}
811:
    def __weel_recursive_continue(thread)# {{{
812:
      return unless thread
813:
      if thread.alive? && thread[:continue]
814:
        thread[:continue].continue
815:
      end
816:
      if thread.alive? && thread[:branch_event]
817:
        thread[:mutex].synchronize do
818:
          thread[:branch_event].continue unless thread[:branch_event].nil?
819:
        end
820:
      end
821:
      if thread[:branches]
822:
        thread[:branches].each do |b|
823:
          __weel_recursive_continue(b)
824:
        end
825:
      end
826:
    end  # }}}
827:
    def __weel_recursive_join(thread)# {{{
828:
      return unless thread
829:
      if thread.alive? && thread != Thread.current
830:
        thread.join
831:
      end
832:
      if thread[:branches]
833:
        thread[:branches].each do |b|
834:
          __weel_recursive_join(b)
835:
        end
836:
      end
837:
    end  # }}}
838:
 
839:
    def __weel_position_test(position)# {{{
840:
      if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
841:
        position
842:
      else
843:
        self.__weel_state = :stopping
844:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("position (#{position}) not valid"),nil)
845:
      end
846:
    end # }}}
847:
 
848:
    def __weel_is_in_search_mode(position = nil)# {{{
849:
      branch = Thread.current
850:
      return false if @__weel_search_positions.empty? || branch[:branch_search] == false
851:
 
852:
      if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
853:
        branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
854:
        while branch.key?(:branch_parent) # also all parent branches should execute activities after this point, additional branches spawned by parent branches should still be in search mode
855:
          branch = branch[:branch_parent]
856:
          branch[:branch_search] = false
857:
        end
858:
        @__weel_search_positions[position].detail == :after ? :after : false
859:
      else
860:
        branch[:branch_search] = true
861:
      end
862:
    end # }}}
863:
 
864:
    def __weel_sim #{{{
865:
      @__weel_state == :simulating
866:
    end #}}}
867:
 
868:
    def __weel_sim_start(what,options={}) #{{{
869:
      current_branch_sim_pos = Thread.current[:branch_sim_pos]
870:
      Thread.current[:branch_sim_pos] = @__weel_sim += 1
871:
      handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
872:
      handlerwrapper.simulate(what,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
873:
      [handlerwrapper, current_branch_sim_pos]
874:
    end #}}}
875:
 
876:
    def __weel_sim_stop(what,handlerwrapper,current_branch_sim_pos,options={}) #{{{
877:
      handlerwrapper.simulate(what,:end,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
878:
      Thread.current[:branch_sim_pos] = current_branch_sim_pos
879:
    end #}}}
880:
 
881:
  public
882:
    def __weel_finalize #{{{
883:
      __weel_recursive_join(@__weel_main)
884:
      @__weel_state = :stopped
885:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
886:
    end #}}}
887:
 
888:
    def __weel_state=(newState)# {{{
889:
      return @__weel_state if newState == @__weel_state && @__weel_state != :ready
890:
 
891:
      @__weel_positions = Array.new if newState == :running
892:
      @__weel_state = newState
893:
 
894:
      if newState == :stopping || newState == :finishing
895:
        @__weel_status.nudge!
896:
        __weel_recursive_continue(@__weel_main)
897:
      end
898:
 
899:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
900:
    end # }}}
901:
 
902:
  end # }}}
903:
 
904:
public
905:
  def positions # {{{
906:
    @dslr.__weel_positions
907:
  end # }}}
908:
 
909:
  # set the handlerwrapper
910:
  def handlerwrapper # {{{
911:
    @dslr.__weel_handlerwrapper
912:
  end # }}}
913:
  def handlerwrapper=(new_weel_handlerwrapper) # {{{
914:
    superclass = new_weel_handlerwrapper
915:
    while superclass
916:
      check_ok = true if superclass == WEEL::HandlerWrapperBase
917:
      superclass = superclass.superclass
918:
    end
919:
    raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
920:
    @dslr.__weel_handlerwrapper = new_weel_handlerwrapper
921:
  end # }}}
922:
 
923:
  # Get/Set the handlerwrapper arguments
924:
  def handlerwrapper_args # {{{
925:
    @dslr.__weel_handlerwrapper_args
926:
  end # }}}
927:
  def handlerwrapper_args=(args) # {{{
928:
    if args.class == Array
929:
      @dslr.__weel_handlerwrapper_args = args
930:
    end
931:
    nil
932:
  end #  }}}
933:
 
934:
  # Get the state of execution (ready|running|stopping|stopped|finished|simulating)
935:
  def state # {{{
936:
    @dslr.__weel_state
937:
  end #  }}}
938:
  def state_signal # {{{
939:
    handlerwrapper::inform_state_change handlerwrapper_args, state
940:
    state
941:
  end # }}}
942:
 
943:
  # Set search positions
944:
  # set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
945:
  def search(new_weel_search=false) # {{{
946:
    @dslr.__weel_search_positions.clear
947:
 
948:
    new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
949:
 
950:
    if !new_weel_search.is_a?(Array) || new_weel_search.empty?
951:
      false
952:
    else
953:
      new_weel_search.each do |search_position|
954:
        @dslr.__weel_search_positions[search_position.position] = search_position
955:
      end
956:
      true
957:
    end
958:
  end # }}}
959:
 
960:
  def data(new_data=nil) # {{{
961:
    unless new_data.nil? || !new_data.is_a?(Hash)
962:
      new_data.each{ |k,v| @dslr.__weel_data[k] = v }
963:
    end
964:
    @dslr.__weel_data
965:
  end # }}}
966:
  def endpoints(new_endpoints=nil) # {{{
967:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
968:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
969:
    end
970:
    @dslr.__weel_endpoints
971:
  end # }}}
972:
  def endpoint(new_endpoints) # {{{
973:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
974:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
975:
    end
976:
    nil
977:
  end # }}}
978:
  def status # {{{
979:
    @dslr.__weel_status
980:
  end # }}}
981:
 
982:
  # get/set workflow description
983:
  def description(&blk)
984:
    self.description=(blk)
985:
  end
986:
  def description=(code) # {{{
987:
    (class << self; self; end).class_eval do
988:
      remove_method :__weel_control_flow if method_defined? :__weel_control_flow
989:
      define_method :__weel_control_flow do |state,final_state=:finished|
990:
        @dslr.__weel_positions.clear
991:
        @dslr.__weel_state = state
992:
        begin
993:
          if code.is_a? Proc
994:
            @dslr.instance_eval(&code)
995:
          else
996:
            @dslr.instance_eval(code)
997:
          end
998:
        rescue SyntaxError => se
999:
          @dslr.__weel_state = :stopping
1000:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(se.message),code)
1001:
        rescue NameError => err # don't look into it, or it will explode
1002:
          @dslr.__weel_state = :stopping
1003:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new("main: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),code)
1004:
        rescue => err
1005:
          @dslr.__weel_state = :stopping
1006:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(err.message),code)
1007:
        end
1008:
        if @dslr.__weel_state == :running || @dslr.__weel_state == :finishing
1009:
          ipc = { :unmark => [] }
1010:
          @dslr.__weel_positions.each{ |wp| ipc[:unmark] << wp.position }
1011:
          @dslr.__weel_positions.clear
1012:
          @dslr.__weel_handlerwrapper::inform_position_change(@dslr.__weel_handlerwrapper_args,ipc)
1013:
          @dslr.__weel_state = :finished
1014:
        end
1015:
        if @dslr.__weel_state == :simulating
1016:
          @dslr.__weel_state = final_state
1017:
        end
1018:
        if @dslr.__weel_state == :stopping
1019:
          @dslr.__weel_finalize
1020:
        end
1021:
      end
1022:
    end
1023:
  end # }}}
1024:
 
1025:
  # Stop the workflow execution
1026:
  def stop # {{{
1027:
    Thread.new do
1028:
      @dslr.__weel_state = :stopping
1029:
      @dslr.__weel_main.join if @dslr.__weel_main
1030:
    end
1031:
  end # }}}
1032:
  # Start the workflow execution
1033:
  def start # {{{
1034:
    return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
1035:
    @dslr.__weel_main = Thread.new do
1036:
      begin
1037:
        __weel_control_flow(:running)
1038:
      rescue => e
1039:
        puts e.message
1040:
        puts e.backtrace
1041:
        handlerwrapper::inform_handlerwrapper_error handlerwrapper_args, e
1042:
      end
1043:
    end
1044:
  end # }}}
1045:
 
1046:
  def sim # {{{
1047:
    stat = @dslr.__weel_state
1048:
    return nil unless stat == :ready || stat == :stopped
1049:
    @dslr.__weel_main = Thread.new do
1050:
      __weel_control_flow :simulating, stat
1051:
    end
1052:
  end # }}}
1053:
 
1054:
end