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