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:
    def self::modify_position_details(arguments); end
219:
 
220:
    def initialize(arguments,endpoint=nil,position=nil,continue=nil); end
221:
 
222:
    def activity_handle(passthrough, parameters); end
223:
    def activity_manipulate_handle(parameters); end
224:
 
225:
    def activity_result_value; end
226:
 
227:
    def activity_stop; end
228:
    def activity_passthrough_value; end
229:
 
230:
    def activity_no_longer_necessary; end
231:
 
232:
    def inform_activity_done; end
233:
    def inform_activity_manipulate; end
234:
    def inform_activity_failed(err); end
235:
    def inform_manipulate_change(status,changed_data,changed_endpoints,data,endpoints); end
236:
 
237:
    def vote_sync_before(parameters=nil); true; end
238:
    def vote_sync_after; true; end
239:
 
240:
    # type       => activity, loop, parallel, choice
241:
    # nesting    => none, start, end
242:
    # eid        => id's also for control structures
243:
    # parameters => stuff given to the control structure
244:
    def simulate(type,nesting,sequence,parent,parameters={}); end
245:
 
246:
    def callback(result=nil,options={}); end
247:
 
248:
    def test_condition(mr,code); mr.instance_eval(code); end
249:
    def manipulate(mr,code,result=nil); mr.instance_eval(code); end
250:
  end  # }}}
251:
 
252:
  class Position # {{{
253:
    attr_reader :position
254:
    attr_accessor :detail, :passthrough
255:
    def initialize(position, detail=:at, passthrough=nil) # :at or :after or :unmark
256:
      @position = position
257:
      @detail = detail
258:
      @passthrough = passthrough
259:
    end
260:
    def as_json(*)
261:
      jsn = { 'position' => @position }
262:
      jsn['passtrough'] = @passthrough if @passthrough
263:
      jsn
264:
    end
265:
    def to_s
266:
      as_json.to_s
267:
    end
268:
    def to_json(*args)
269:
      as_json.to_json(*args)
270:
    end
271:
  end # }}}
272:
 
273:
   class Continue # {{{
274:
     def initialize
275:
       @q = Queue.new
276:
       @m = Mutex.new
277:
     end
278:
     def waiting?
279:
       @m.synchronize do
280:
         !@q.empty?
281:
       end
282:
     end
283:
     def continue(*args)
284:
       @q.push(args.length <= 1 ? args[0] : args)
285:
     end
286:
     def clear
287:
      @q.clear
288:
     end
289:
     def wait
290:
       @q.deq
291:
     end
292:
   end #}}}
293:
 
294:
  def self::search(weel_search)# {{{
295:
    define_method :initialize_search do
296:
      self.search weel_search
297:
    end
298:
  end # }}}
299:
  def self::endpoint(new_endpoints)# {{{
300:
    @@__weel_new_endpoints ||= {}
301:
    @@__weel_new_endpoints.merge! new_endpoints
302:
    remove_method :initialize_endpoints if method_defined? :initialize_endpoints
303:
    define_method :initialize_endpoints do
304:
      @@__weel_new_endpoints.each do |name,value|
305:
        @dslr.__weel_endpoints[name.to_s.to_sym] = value
306:
      end
307:
    end
308:
  end # }}}
309:
  def self::data(data_elements)# {{{
310:
    @@__weel_new_data_elements ||= {}
311:
    @@__weel_new_data_elements.merge! data_elements
312:
    define_method :initialize_data do
313:
      @@__weel_new_data_elements.each do |name,value|
314:
        @dslr.__weel_data[name.to_s.to_sym] = value
315:
      end
316:
    end
317:
  end # }}}
318:
  def self::handlerwrapper(aClassname, *args)# {{{
319:
    define_method :initialize_handlerwrapper do
320:
      self.handlerwrapper = aClassname
321:
      self.handlerwrapper_args = args unless args.empty?
322:
    end
323:
  end # }}}
324:
  def self::control(flow, &block)# {{{
325:
    @@__weel_control_block = block
326:
    define_method :initialize_control do
327:
      self.description = @@__weel_control_block
328:
    end
329:
  end #  }}}
330:
  def self::flow # {{{
331:
  end #}}}
332:
 
333:
  class DSLRealization # {{{
334:
    def  initialize #{{{
335:
      @__weel_search_positions = {}
336:
      @__weel_positions = Array.new
337:
      @__weel_main = nil
338:
      @__weel_data ||= Hash.new
339:
      @__weel_endpoints ||= Hash.new
340:
      @__weel_handlerwrapper = HandlerWrapperBase
341:
      @__weel_handlerwrapper_args = []
342:
      @__weel_state = :ready
343:
      @__weel_status = Status.new(0,"undefined")
344:
      @__weel_sim = -1
345:
    end #}}}
346:
    attr_accessor :__weel_search_positions, :__weel_positions, :__weel_main, :__weel_data, :__weel_endpoints, :__weel_handlerwrapper, :__weel_handlerwrapper_args
347:
    attr_reader :__weel_state, :__weel_status
348:
 
349:
    # DSL-Constructs for atomic calls to external services (calls) and pure context manipulations (manipulate).
350:
    # Calls can also manipulate context (after the invoking the external services)
351:
    # position: a unique identifier within the wf-description (may be used by the search to identify a starting point)
352:
    # endpoint: (only with :call) ep of the service
353:
    # parameters: (only with :call) service parameters
354:
    def call(position, endpoint, parameters: {}, finalize: nil, update: nil, &finalizeblk) #{{{
355:
      __weel_activity(position,:call,endpoint,parameters,finalize||finalizeblk,update)
356:
    end #}}}
357:
    # when two params, second param always script
358:
    # when block and two params, parameters stays
359:
    def manipulate(position, parameters=nil, script=nil, &scriptblk) #{{{
360:
      if scriptblk.nil? && script.nil? && !parameters.nil?
361:
        script, parameters = parameters, nil
362:
      end
363:
      __weel_activity(position,:manipulate,nil,parameters||{},script||scriptblk)
364:
    end #}}}
365:
 
366:
    # Parallel DSL-Construct
367:
    # Defines Workflow paths that can be executed parallel.
368:
    # May contain multiple branches (parallel_branch)
369:
    def parallel(type=nil)# {{{
370:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
371:
 
372:
      Thread.current[:branches] = []
373:
      Thread.current[:branch_finished_count] = 0
374:
      Thread.current[:branch_event] = Continue.new
375:
      Thread.current[:mutex] = Mutex.new
376:
 
377:
      hw, pos = __weel_sim_start(:parallel) if __weel_sim
378:
 
379:
      __weel_protect_yield(&Proc.new)
380:
 
381:
      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)
382:
      1.upto Thread.current[:branches].size do
383:
        Thread.current[:branch_event].wait
384:
      end
385:
 
386:
      Thread.current[:branches].each do |thread|
387:
        # decide after executing block in parallel cause for coopis
388:
        # it goes out of search mode while dynamically counting branches
389:
        if Thread.current[:branch_search] == false
390:
          thread[:branch_search] = false
391:
        end
392:
        thread[:start_event].continue
393:
      end
394:
 
395:
      Thread.current[:branch_event].wait
396:
 
397:
      __weel_sim_stop(:parallel,hw,pos) if __weel_sim
398:
 
399:
      unless self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
400:
        # first set all to no_longer_neccessary
401:
        Thread.current[:branches].each do |thread|
402:
          if thread.alive?
403:
            thread[:nolongernecessary] = true
404:
            __weel_recursive_continue(thread)
405:
          end
406:
        end
407:
        # wait for all
408:
        Thread.current[:branches].each do |thread|
409:
          __weel_recursive_join(thread)
410:
        end
411:
      end
412:
    end # }}}
413:
 
414:
    # Defines a branch of a parallel-Construct
415:
    def parallel_branch(*vars)# {{{
416:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
417:
      branch_parent = Thread.current
418:
 
419:
      if __weel_sim
420:
        # catch the potential execution in loops inside a parallel
421:
        current_branch_sim_pos = branch_parent[:branch_sim_pos]
422:
      end
423:
 
424:
      Thread.current[:branches] << Thread.new(*vars) do |*local|
425:
        Thread.current.abort_on_exception = true
426:
        Thread.current[:branch_status] = false
427:
        Thread.current[:branch_parent] = branch_parent
428:
        Thread.current[:start_event] = Continue.new
429:
 
430:
        if __weel_sim
431:
          Thread.current[:branch_sim_pos] = @__weel_sim += 1
432:
        end
433:
 
434:
        # parallel_branch could be possibly around an alternative. Thus thread has to inherit the alternative_executed
435:
        # after branching, update it in the parent (TODO)
436:
        if branch_parent[:alternative_executed] && branch_parent[:alternative_executed].length > 0
437:
          Thread.current[:alternative_executed] = [branch_parent[:alternative_executed].last]
438:
          Thread.current[:alternative_mode] = [branch_parent[:alternative_mode].last]
439:
        end
440:
        branch_parent[:branch_event].continue
441:
        Thread.current[:start_event].wait
442:
 
443:
        if __weel_sim
444:
          handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
445:
          handlerwrapper.simulate(:parallel_branch,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos)
446:
        end
447:
 
448:
        __weel_protect_yield(*local, &Proc.new)
449:
 
450:
        __weel_sim_stop(:parallel_branch,handlerwrapper,current_branch_sim_pos) if __weel_sim
451:
 
452:
        branch_parent[:mutex].synchronize do
453:
          Thread.current[:branch_status] = true
454:
          branch_parent[:branch_finished_count] += 1
455:
          if branch_parent[:branch_finished_count] == branch_parent[:branch_wait_count] && self.__weel_state != :stopping && self.__weel_state != :finishing
456:
            branch_parent[:branch_event].continue
457:
          end
458:
        end
459:
        if self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
460:
          if Thread.current[:branch_position]
461:
            @__weel_positions.delete Thread.current[:branch_position]
462:
            begin
463:
              ipc = {}
464:
              ipc[:unmark] = [Thread.current[:branch_position]]
465:
              @__weel_handlerwrapper::inform_position_change(@__weel_handlerwrapper_args,ipc)
466:
            end rescue nil
467:
            Thread.current[:branch_position] = nil
468:
          end
469:
        end
470:
      end
471:
    end # }}}
472:
 
473:
    # Choose DSL-Construct
474:
    # Defines a choice in the Workflow path.
475:
    # May contain multiple execution alternatives
476:
    def choose(mode=:inclusive) # {{{
477:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
478:
      Thread.current[:alternative_executed] ||= []
479:
      Thread.current[:alternative_mode] ||= []
480:
      Thread.current[:alternative_executed] << false
481:
      Thread.current[:alternative_mode] << mode
482:
      hw, pos = __weel_sim_start(:choose,:mode => Thread.current[:alternative_mode].last) if __weel_sim
483:
      __weel_protect_yield(&Proc.new)
484:
      __weel_sim_stop(:choose,hw,pos,:mode => Thread.current[:alternative_mode].last) if __weel_sim
485:
      Thread.current[:alternative_executed].pop
486:
      Thread.current[:alternative_mode].pop
487:
      nil
488:
    end # }}}
489:
 
490:
    # Defines a possible choice of a choose-Construct
491:
    # Block is executed if condition == true or
492:
    # searchmode is active (to find the starting position)
493:
    def alternative(condition,args={})# {{{
494:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
495:
      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
496:
      Thread.current[:mutex] ||= Mutex.new
497:
      Thread.current[:mutex].synchronize do
498:
        return if Thread.current[:alternative_mode][-1] == :exclusive && Thread.current[:alternative_executed][-1] == true
499:
        if (condition.is_a?(String) || condition.is_a?(Proc)) && !__weel_sim
500:
          condition = __weel_eval_condition(condition)
501:
        end
502:
        Thread.current[:alternative_executed][-1] = true if condition
503:
      end
504:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || condition
505:
      __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
506:
    end # }}}
507:
    def otherwise(args={}) # {{{
508:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
509:
      hw, pos = __weel_sim_start(:otherwise,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
510:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || !Thread.current[:alternative_executed].last
511:
      __weel_sim_stop(:otherwise,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
512:
    end # }}}
513:
 
514:
    # Defines a critical block (=Mutex)
515:
    def critical(id)# {{{
516:
      @__weel_critical ||= Mutex.new
517:
      semaphore = nil
518:
      @__weel_critical.synchronize do
519:
        @__weel_critical_sections ||= {}
520:
        semaphore = @__weel_critical_sections[id] ? @__weel_critical_sections[id] : Mutex.new
521:
        @__weel_critical_sections[id] = semaphore if id
522:
      end
523:
      semaphore.synchronize do
524:
        __weel_protect_yield(&Proc.new)
525:
      end
526:
    end # }}}
527:
 
528:
    # Defines a Cycle (loop/iteration)
529:
    def loop(condition,args={})# {{{
530:
      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)
531:
        raise "condition must be called pre_test{} or post_test{}"
532:
      end
533:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
534:
      if __weel_is_in_search_mode
535:
        catch :escape do
536:
          __weel_protect_yield(&Proc.new)
537:
        end
538:
        if __weel_is_in_search_mode
539:
          return
540:
        else
541:
          ### in case it was a :post_test we wake inside the loop so we can check
542:
          ### condition first thing
543:
          condition[1] = :pre_test
544:
        end
545:
      end
546:
      if __weel_sim
547:
        cond = condition[0].is_a?(Proc) ? true : condition[0]
548:
        hw, pos = __weel_sim_start(:loop,args.merge(:testing=>condition[1],:condition=>cond))
549:
        catch :escape do
550:
          __weel_protect_yield(&Proc.new)
551:
        end
552:
        __weel_sim_stop(:loop,hw,pos,args.merge(:testing=>condition[1],:condition=>cond))
553:
        return
554:
      end
555:
      catch :escape do
556:
        case condition[1]
557:
          when :pre_test
558:
            while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
559:
              __weel_protect_yield(&Proc.new)
560:
            end
561:
          when :post_test
562:
            begin
563:
              __weel_protect_yield(&Proc.new)
564:
            end while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
565:
        end
566:
      end
567:
    end # }}}
568:
 
569:
    def test(code=nil,&blk)# {{{
570:
      code || blk
571:
    end # }}}
572:
    def pre_test(code=nil,&blk)# {{{
573:
      [code || blk, :pre_test]
574:
    end # }}}
575:
    def post_test(code=nil,&blk)# {{{
576:
      [code || blk, :post_test]
577:
    end # }}}
578:
 
579:
    def escape #{{{
580:
      return if __weel_is_in_search_mode
581:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
582:
      throw :escape
583:
    end #}}}
584:
    def terminate #{{{
585:
      return if __weel_is_in_search_mode
586:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
587:
      self.__weel_state = :finishing
588:
    end #}}}
589:
    def stop(position) #{{{
590:
      searchmode = __weel_is_in_search_mode(position)
591:
      return if searchmode
592:
      return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
593:
      __weel_progress searchmode, position, true
594:
      self.__weel_state = :stopping
595:
    end #}}}
596:
 
597:
    def status # {{{
598:
      @__weel_status
599:
    end # }}}
600:
    def data # {{{
601:
      ReadHash.new(@__weel_data,__weel_sim)
602:
    end # }}}
603:
    def endpoints # {{{
604:
      ReadHash.new(@__weel_endpoints)
605:
    end # }}}
606:
 
607:
  private
608:
    def __weel_protect_yield(*local) #{{{
609:
      begin
610:
        yield(*local) if block_given?
611:
      rescue NameError => err # don't look into it, or it will explode
612:
        self.__weel_state = :stopping
613:
        @__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)
614:
        nil
615:
      rescue => err
616:
        self.__weel_state = :stopping
617:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
618:
        nil
619:
      end
620:
    end #}}}
621:
 
622:
    def __weel_eval_condition(condition) #{{{
623:
      begin
624:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args unless condition.is_a?(Proc)
625:
        condition.is_a?(Proc) ? condition.call : handlerwrapper.test_condition(ReadStructure.new(@__weel_data,@__weel_endpoints),condition)
626:
      rescue NameError => err # don't look into it, or it will explode
627:
        # if you access $! here, BOOOM
628:
        self.__weel_state = :stopping
629:
        @__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)
630:
        nil
631:
      rescue => err
632:
        self.__weel_state = :stopping
633:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
634:
        nil
635:
      end
636:
    end #}}}
637:
 
638:
    def __weel_progress(searchmode, position, skip=false) #{{{
639:
      ipc = {}
640:
      if searchmode == :after
641:
        wp = WEEL::Position.new(position, :after, nil)
642:
        ipc[:after] = [wp]
643:
      else
644:
        if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
645:
          @__weel_positions.delete Thread.current[:branch_parent][:branch_position]
646:
          ipc[:unmark] ||= []
647:
          ipc[:unmark] << Thread.current[:branch_parent][:branch_position] rescue nil
648:
          Thread.current[:branch_parent][:branch_position] = nil
649:
        end
650:
        if Thread.current[:branch_position]
651:
          @__weel_positions.delete Thread.current[:branch_position]
652:
          ipc[:unmark] ||= []
653:
          ipc[:unmark] << Thread.current[:branch_position] rescue nil
654:
        end
655:
        wp = WEEL::Position.new(position, skip ? :after : :at, nil)
656:
        ipc[skip ? :after : :at] = [wp]
657:
      end
658:
      @__weel_positions << wp
659:
      Thread.current[:branch_position] = wp
660:
 
661:
      @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, ipc
662:
      wp
663:
    end #}}}
664:
 
665:
    def __weel_activity(position, type, endpoints, parameters, finalize, update=nil)# {{{
666:
      position = __weel_position_test position
667:
      begin
668:
        searchmode = __weel_is_in_search_mode(position)
669:
        return if searchmode == true
670:
        return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
671:
 
672:
        Thread.current[:continue] = Continue.new
673:
        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]
674:
 
675:
        if __weel_sim
676:
          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)
677:
          return
678:
        end
679:
 
680:
        wp = __weel_progress searchmode, position
681:
 
682:
        # searchmode position is after, jump directly to vote_sync_after
683:
        raise Signal::Proceed if searchmode == :after
684:
 
685:
        case type
686:
          when :manipulate
687:
            raise Signal::Stop unless handlerwrapper.vote_sync_before
688:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
689:
 
690:
            if finalize.is_a?(Proc) || finalize.is_a?(String)
691:
              handlerwrapper.activity_manipulate_handle(parameters)
692:
              handlerwrapper.inform_activity_manipulate
693:
              if finalize.is_a?(Proc)
694:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
695:
                mr.instance_eval(&finalize)
696:
              elsif finalize.is_a?(String)
697:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
698:
                handlerwrapper.manipulate(mr,finalize)
699:
              end
700:
              handlerwrapper.inform_manipulate_change(
701:
                ((mr && mr.changed_status) ? @__weel_status : nil),
702:
                ((mr && mr.changed_data.any?) ? mr.changed_data.uniq : nil),
703:
                ((mr && mr.changed_endpoints.any?) ? mr.changed_endpoints.uniq : nil),
704:
                @__weel_data,
705:
                @__weel_endpoints
706:
              )
707:
              handlerwrapper.inform_activity_done
708:
              wp.detail = :after
709:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp]
710:
            end
711:
          when :call
712:
            params = { }
713:
            case parameters
714:
              when Hash
715:
                parameters.each do |k,p|
716:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
717:
                    params[k] = @__weel_data[p]
718:
                  else
719:
                    params[k] = p
720:
                  end
721:
                end
722:
              when Array
723:
                parameters.each_with_index do |p,i|
724:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
725:
                    params[p] = @__weel_data[p]
726:
                  else
727:
                    params[i] = p
728:
                  end
729:
                end
730:
              else
731:
                raise("invalid parameters")
732:
            end
733:
            raise Signal::Stop unless handlerwrapper.vote_sync_before(params)
734:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
735:
 
736:
            if @__weel_search_positions[position]
737:
              passthrough = @__weel_search_positions[position].passthrough
738:
              @__weel_search_positions[position].passthrough = nil
739:
            else
740:
              passthrough = nil
741:
            end
742:
 
743:
            handlerwrapper.activity_handle passthrough, params
744:
            wp.passthrough = handlerwrapper.activity_passthrough_value
745:
            unless wp.passthrough.nil?
746:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :wait => [wp]
747:
            end
748:
            begin
749:
              # with loop if catching Signal::Again
750:
              # handshake call and wait until it finished
751:
              waitingresult = nil
752:
              waitingresult = Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
753:
              raise waitingresult[1] if !waitingresult.nil? && waitingresult.is_a?(Array) && waitingresult.length == 2 && waitingresult[0] == WEEL::Signal::Error
754:
 
755:
              if Thread.current[:nolongernecessary]
756:
                handlerwrapper.activity_no_longer_necessary
757:
                raise Signal::NoLongerNecessary
758:
              end
759:
              if self.__weel_state == :stopping || self.__weel_state == :finishing
760:
                handlerwrapper.activity_stop
761:
                wp.passthrough = handlerwrapper.activity_passthrough_value
762:
                raise Signal::Proceed
763:
              end
764:
 
765:
              code = waitingresult == Signal::Again ? update : finalize
766:
              if code.is_a?(Proc) || code.is_a?(String)
767:
                handlerwrapper.inform_activity_manipulate
768:
                if code.is_a?(Proc)
769:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
770:
                  case code.arity
771:
                    when 1; mr.instance_exec(handlerwrapper.activity_result_value,&code)
772:
                    when 2; mr.instance_exec(handlerwrapper.activity_result_value,&code)
773:
                    else
774:
                      mr.instance_exec(&code)
775:
                  end
776:
                elsif code.is_a?(String)
777:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
778:
                  handlerwrapper.manipulate(mr,code,handlerwrapper.activity_result_value)
779:
                end
780:
                handlerwrapper.inform_manipulate_change(
781:
                  (mr.changed_status ? @__weel_status : nil),
782:
                  (mr.changed_data.any? ? mr.changed_data.uniq : nil),
783:
                  (mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil),
784:
                  @__weel_data,
785:
                  @__weel_endpoints
786:
                )
787:
              end
788:
            end while waitingresult == Signal::Again
789:
            if handlerwrapper.activity_passthrough_value.nil?
790:
              handlerwrapper.inform_activity_done
791:
              wp.passthrough = nil
792:
              wp.detail = :after
793:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp]
794:
            end
795:
        end
796:
        raise Signal::Proceed
797:
      rescue Signal::SkipManipulate, Signal::Proceed
798:
        if self.__weel_state != :stopping && self.__weel_state != :finishing && !handlerwrapper.vote_sync_after
799:
          self.__weel_state = :stopping
800:
          wp.detail = :unmark
801:
        end
802:
      rescue Signal::NoLongerNecessary
803:
        @__weel_positions.delete wp
804:
        Thread.current[:branch_position] = nil
805:
        wp.detail = :unmark
806:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :unmark => [wp]
807:
      rescue Signal::StopSkipManipulate, Signal::Stop
808:
        self.__weel_state = :stopping
809:
      rescue Signal::Skip
810:
        nil
811:
      rescue SyntaxError => se
812:
        handlerwrapper.inform_activity_failed se
813:
        self.__weel_state = :stopping
814:
      rescue => err
815:
        handlerwrapper.inform_activity_failed err
816:
        self.__weel_state = :stopping
817:
      ensure
818:
        Thread.current[:continue].clear if Thread.current[:continue] && Thread.current[:continue].is_a?(Continue)
819:
      end
820:
    end # }}}
821:
 
822:
    def __weel_recursive_print(thread,indent='')# {{{
823:
      p "#{indent}#{thread}"
824:
      if thread[:branches]
825:
        thread[:branches].each do |b|
826:
          __weel_recursive_print(b,indent+'  ')
827:
        end
828:
      end
829:
    end  # }}}
830:
    def __weel_recursive_continue(thread)# {{{
831:
      return unless thread
832:
      if thread.alive? && thread[:continue]
833:
        thread[:continue].continue
834:
      end
835:
      if thread.alive? && thread[:branch_event]
836:
        thread[:mutex].synchronize do
837:
          thread[:branch_event].continue unless thread[:branch_event].nil?
838:
        end
839:
      end
840:
      if thread[:branches]
841:
        thread[:branches].each do |b|
842:
          __weel_recursive_continue(b)
843:
        end
844:
      end
845:
    end  # }}}
846:
    def __weel_recursive_join(thread)# {{{
847:
      return unless thread
848:
      if thread.alive? && thread != Thread.current
849:
        thread.join
850:
      end
851:
      if thread[:branches]
852:
        thread[:branches].each do |b|
853:
          __weel_recursive_join(b)
854:
        end
855:
      end
856:
    end  # }}}
857:
 
858:
    def __weel_position_test(position)# {{{
859:
      if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
860:
        position
861:
      else
862:
        self.__weel_state = :stopping
863:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("position (#{position}) not valid"),nil)
864:
      end
865:
    end # }}}
866:
 
867:
    def __weel_is_in_search_mode(position = nil)# {{{
868:
      branch = Thread.current
869:
      return false if @__weel_search_positions.empty? || branch[:branch_search] == false
870:
 
871:
      if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
872:
        branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
873:
        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
874:
          branch = branch[:branch_parent]
875:
          branch[:branch_search] = false
876:
        end
877:
        @__weel_search_positions[position].detail == :after ? :after : false
878:
      else
879:
        branch[:branch_search] = true
880:
      end
881:
    end # }}}
882:
 
883:
    def __weel_sim #{{{
884:
      @__weel_state == :simulating
885:
    end #}}}
886:
 
887:
    def __weel_sim_start(what,options={}) #{{{
888:
      current_branch_sim_pos = Thread.current[:branch_sim_pos]
889:
      Thread.current[:branch_sim_pos] = @__weel_sim += 1
890:
      handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
891:
      handlerwrapper.simulate(what,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
892:
      [handlerwrapper, current_branch_sim_pos]
893:
    end #}}}
894:
 
895:
    def __weel_sim_stop(what,handlerwrapper,current_branch_sim_pos,options={}) #{{{
896:
      handlerwrapper.simulate(what,:end,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
897:
      Thread.current[:branch_sim_pos] = current_branch_sim_pos
898:
    end #}}}
899:
 
900:
  public
901:
    def __weel_finalize #{{{
902:
      __weel_recursive_join(@__weel_main)
903:
      @__weel_state = :stopped
904:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
905:
    end #}}}
906:
 
907:
    def __weel_state=(newState)# {{{
908:
      return @__weel_state if newState == @__weel_state && @__weel_state != :ready
909:
 
910:
      @__weel_positions = Array.new if newState == :running
911:
      @__weel_state = newState
912:
 
913:
      if newState == :stopping || newState == :finishing
914:
        @__weel_status.nudge!
915:
        __weel_recursive_continue(@__weel_main)
916:
      end
917:
 
918:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
919:
    end # }}}
920:
 
921:
  end # }}}
922:
 
923:
public
924:
  def positions # {{{
925:
    @dslr.__weel_positions
926:
  end # }}}
927:
 
928:
  # set the handlerwrapper
929:
  def handlerwrapper # {{{
930:
    @dslr.__weel_handlerwrapper
931:
  end # }}}
932:
  def handlerwrapper=(new_weel_handlerwrapper) # {{{
933:
    superclass = new_weel_handlerwrapper
934:
    while superclass
935:
      check_ok = true if superclass == WEEL::HandlerWrapperBase
936:
      superclass = superclass.superclass
937:
    end
938:
    raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
939:
    @dslr.__weel_handlerwrapper = new_weel_handlerwrapper
940:
  end # }}}
941:
 
942:
  # Get/Set the handlerwrapper arguments
943:
  def handlerwrapper_args # {{{
944:
    @dslr.__weel_handlerwrapper_args
945:
  end # }}}
946:
  def handlerwrapper_args=(args) # {{{
947:
    if args.class == Array
948:
      @dslr.__weel_handlerwrapper_args = args
949:
    end
950:
    nil
951:
  end #  }}}
952:
 
953:
  # Get the state of execution (ready|running|stopping|stopped|finished|simulating)
954:
  def state # {{{
955:
    @dslr.__weel_state
956:
  end #  }}}
957:
  def state_signal # {{{
958:
    handlerwrapper::inform_state_change handlerwrapper_args, state
959:
    state
960:
  end # }}}
961:
 
962:
  # Set search positions
963:
  # set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
964:
  def search(new_weel_search=false) # {{{
965:
    @dslr.__weel_search_positions.clear
966:
 
967:
    new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
968:
 
969:
    if !new_weel_search.is_a?(Array) || new_weel_search.empty?
970:
      false
971:
    else
972:
      new_weel_search.each do |search_position|
973:
        @dslr.__weel_search_positions[search_position.position] = search_position
974:
      end
975:
      true
976:
    end
977:
  end # }}}
978:
 
979:
  def data(new_data=nil) # {{{
980:
    unless new_data.nil? || !new_data.is_a?(Hash)
981:
      new_data.each{ |k,v| @dslr.__weel_data[k] = v }
982:
    end
983:
    @dslr.__weel_data
984:
  end # }}}
985:
  def endpoints(new_endpoints=nil) # {{{
986:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
987:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
988:
    end
989:
    @dslr.__weel_endpoints
990:
  end # }}}
991:
  def endpoint(new_endpoints) # {{{
992:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
993:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
994:
    end
995:
    nil
996:
  end # }}}
997:
  def status # {{{
998:
    @dslr.__weel_status
999:
  end # }}}
1000:
 
1001:
  # get/set workflow description
1002:
  def description(&blk)
1003:
    self.description=(blk)
1004:
  end
1005:
  def description=(code) # {{{
1006:
    (class << self; self; end).class_eval do
1007:
      remove_method :__weel_control_flow if method_defined? :__weel_control_flow
1008:
      define_method :__weel_control_flow do |state,final_state=:finished|
1009:
        @dslr.__weel_positions.clear
1010:
        @dslr.__weel_state = state
1011:
        begin
1012:
          if code.is_a? Proc
1013:
            @dslr.instance_eval(&code)
1014:
          else
1015:
            @dslr.instance_eval(code)
1016:
          end
1017:
        rescue SyntaxError => se
1018:
          @dslr.__weel_state = :stopping
1019:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(se.message),code)
1020:
        rescue NameError => err # don't look into it, or it will explode
1021:
          @dslr.__weel_state = :stopping
1022:
          @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)
1023:
        rescue => err
1024:
          @dslr.__weel_state = :stopping
1025:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(err.message),code)
1026:
        end
1027:
        if @dslr.__weel_state == :running || @dslr.__weel_state == :finishing
1028:
          ipc = { :unmark => [] }
1029:
          @dslr.__weel_positions.each{ |wp| ipc[:unmark] << wp }
1030:
          @dslr.__weel_positions.clear
1031:
          @dslr.__weel_handlerwrapper::inform_position_change(@dslr.__weel_handlerwrapper_args,ipc)
1032:
          @dslr.__weel_state = :finished
1033:
        end
1034:
        if @dslr.__weel_state == :simulating
1035:
          @dslr.__weel_state = final_state
1036:
        end
1037:
        if @dslr.__weel_state == :stopping
1038:
          @dslr.__weel_finalize
1039:
        end
1040:
      end
1041:
    end
1042:
  end # }}}
1043:
 
1044:
  # Stop the workflow execution
1045:
  def stop # {{{
1046:
    Thread.new do
1047:
      @dslr.__weel_state = :stopping
1048:
      @dslr.__weel_main.join if @dslr.__weel_main
1049:
    end
1050:
  end # }}}
1051:
  # Start the workflow execution
1052:
  def start # {{{
1053:
    return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
1054:
    @dslr.__weel_main = Thread.new do
1055:
      begin
1056:
        __weel_control_flow(:running)
1057:
      rescue => e
1058:
        puts e.message
1059:
        puts e.backtrace
1060:
        handlerwrapper::inform_handlerwrapper_error handlerwrapper_args, e
1061:
      end
1062:
    end
1063:
  end # }}}
1064:
 
1065:
  def sim # {{{
1066:
    stat = @dslr.__weel_state
1067:
    return nil unless stat == :ready || stat == :stopped
1068:
    @dslr.__weel_main = Thread.new do
1069:
      __weel_control_flow :simulating, stat
1070:
    end
1071:
  end # }}}
1072:
 
1073:
end