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:
        return if __weel_is_in_search_mode
527:
      end
528:
      if __weel_sim
529:
        cond = condition[0].is_a?(Proc) ? true : condition[0]
530:
        hw, pos = __weel_sim_start(:loop,args.merge(:testing=>condition[1],:condition=>cond))
531:
        catch :escape do
532:
          __weel_protect_yield(&Proc.new)
533:
        end
534:
        __weel_sim_stop(:loop,hw,pos,args.merge(:testing=>condition[1],:condition=>cond))
535:
        return
536:
      end
537:
      catch :escape do
538:
        case condition[1]
539:
          when :pre_test
540:
            while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
541:
              __weel_protect_yield(&Proc.new)
542:
            end
543:
          when :post_test
544:
            begin
545:
              __weel_protect_yield(&Proc.new)
546:
            end while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped && self.__weel_state != :finishing
547:
        end
548:
      end
549:
    end # }}}
550:
 
551:
    def test(code=nil,&blk)# {{{
552:
      code || blk
553:
    end # }}}
554:
    def pre_test(code=nil,&blk)# {{{
555:
      [code || blk, :pre_test]
556:
    end # }}}
557:
    def post_test(code=nil,&blk)# {{{
558:
      [code || blk, :post_test]
559:
    end # }}}
560:
 
561:
    def escape #{{{
562:
      return if __weel_is_in_search_mode
563:
      throw :escape
564:
    end #}}}
565:
    def terminate #{{{
566:
      return if __weel_is_in_search_mode
567:
      self.__weel_state = :finishing
568:
    end #}}}
569:
    def stop(position) #{{{
570:
      searchmode = __weel_is_in_search_mode(position)
571:
      return if searchmode
572:
      __weel_progress searchmode, position, true
573:
      self.__weel_state = :stopping
574:
    end #}}}
575:
 
576:
    def status # {{{
577:
      @__weel_status
578:
    end # }}}
579:
    def data # {{{
580:
      ReadHash.new(@__weel_data,__weel_sim)
581:
    end # }}}
582:
    def endpoints # {{{
583:
      ReadHash.new(@__weel_endpoints)
584:
    end # }}}
585:
 
586:
  private
587:
    def __weel_protect_yield(*local) #{{{
588:
      begin
589:
        yield(*local) if block_given?
590:
      rescue NameError => err # don't look into it, or it will explode
591:
        self.__weel_state = :stopping
592:
        @__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)
593:
        nil
594:
      rescue => err
595:
        self.__weel_state = :stopping
596:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
597:
        nil
598:
      end
599:
    end #}}}
600:
 
601:
    def __weel_eval_condition(condition) #{{{
602:
      begin
603:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args unless condition.is_a?(Proc)
604:
        condition.is_a?(Proc) ? condition.call : handlerwrapper.test_condition(ReadStructure.new(@__weel_data,@__weel_endpoints),condition)
605:
      rescue NameError => err # don't look into it, or it will explode
606:
        # if you access $! here, BOOOM
607:
        self.__weel_state = :stopping
608:
        @__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)
609:
        nil
610:
      rescue => err
611:
        self.__weel_state = :stopping
612:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
613:
        nil
614:
      end
615:
    end #}}}
616:
 
617:
    def __weel_progress(searchmode, position, skip=false) #{{{
618:
      ipc = {}
619:
      if searchmode == :after
620:
        wp = WEEL::Position.new(position, :after, nil)
621:
        ipc[:after] = [wp.position]
622:
      else
623:
        if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
624:
          @__weel_positions.delete Thread.current[:branch_parent][:branch_position]
625:
          ipc[:unmark] ||= []
626:
          ipc[:unmark] << Thread.current[:branch_parent][:branch_position].position rescue nil
627:
          Thread.current[:branch_parent][:branch_position] = nil
628:
        end
629:
        if Thread.current[:branch_position]
630:
          @__weel_positions.delete Thread.current[:branch_position]
631:
          ipc[:unmark] ||= []
632:
          ipc[:unmark] << Thread.current[:branch_position].position rescue nil
633:
        end
634:
        wp = WEEL::Position.new(position, skip ? :after : :at, nil)
635:
        ipc[skip ? :after : :at] = [wp.position]
636:
      end
637:
      @__weel_positions << wp
638:
      Thread.current[:branch_position] = wp
639:
 
640:
      @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, ipc
641:
      wp
642:
    end #}}}
643:
 
644:
    def __weel_activity(position, type, endpoints, parameters, finalize, update=nil)# {{{
645:
      position = __weel_position_test position
646:
      begin
647:
        searchmode = __weel_is_in_search_mode(position)
648:
        return if searchmode == true
649:
        return if self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
650:
 
651:
        Thread.current[:continue] = Continue.new
652:
        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]
653:
 
654:
        if __weel_sim
655:
          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)
656:
          return
657:
        end
658:
 
659:
        wp = __weel_progress searchmode, position
660:
 
661:
        # searchmode position is after, jump directly to vote_sync_after
662:
        raise Signal::Proceed if searchmode == :after
663:
 
664:
        case type
665:
          when :manipulate
666:
            raise Signal::Stop unless handlerwrapper.vote_sync_before
667:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
668:
 
669:
            if finalize.is_a?(Proc) || finalize.is_a?(String)
670:
              handlerwrapper.activity_manipulate_handle(parameters)
671:
              handlerwrapper.inform_activity_manipulate
672:
              if finalize.is_a?(Proc)
673:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
674:
                mr.instance_eval(&finalize)
675:
              elsif finalize.is_a?(String)
676:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
677:
                handlerwrapper.manipulate(mr,finalize)
678:
              end
679:
              handlerwrapper.inform_manipulate_change(
680:
                ((mr && mr.changed_status) ? @__weel_status : nil),
681:
                ((mr && mr.changed_data.any?) ? mr.changed_data.uniq : nil),
682:
                ((mr && mr.changed_endpoints.any?) ? mr.changed_endpoints.uniq : nil),
683:
                @__weel_data,
684:
                @__weel_endpoints
685:
              )
686:
              handlerwrapper.inform_activity_done
687:
              wp.detail = :after
688:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
689:
            end
690:
          when :call
691:
            params = { }
692:
            case parameters
693:
              when Hash
694:
                parameters.each do |k,p|
695:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
696:
                    params[k] = @__weel_data[p]
697:
                  else
698:
                    params[k] = p
699:
                  end
700:
                end
701:
              when Array
702:
                parameters.each_with_index do |p,i|
703:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
704:
                    params[p] = @__weel_data[p]
705:
                  else
706:
                    params[i] = p
707:
                  end
708:
                end
709:
              else
710:
                raise("invalid parameters")
711:
            end
712:
            raise Signal::Stop unless handlerwrapper.vote_sync_before(params)
713:
            raise Signal::Skip if self.__weel_state == :stopping || self.__weel_state == :finishing
714:
 
715:
            passthrough = @__weel_search_positions[position] ? @__weel_search_positions[position].passthrough : nil
716:
            handlerwrapper.activity_handle passthrough, params
717:
            begin
718:
              # with loop if catching Signal::Again
719:
              # handshake call and wait until it finished
720:
              waitingresult = nil
721:
              waitingresult = Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :finishing || self.__weel_state == :stopped
722:
              raise waitingresult[1] if !waitingresult.nil? && waitingresult.is_a?(Array) && waitingresult.length == 2 && waitingresult[0] == WEEL::Signal::Error
723:
 
724:
              if Thread.current[:nolongernecessary]
725:
                handlerwrapper.activity_no_longer_necessary
726:
                raise Signal::NoLongerNecessary
727:
              end
728:
              if self.__weel_state == :stopping || self.__weel_state == :finishing
729:
                handlerwrapper.activity_stop
730:
                wp.passthrough = handlerwrapper.activity_passthrough_value
731:
                raise Signal::Proceed
732:
              end
733:
 
734:
              code = waitingresult == Signal::Again ? update : finalize
735:
              if code.is_a?(Proc) || code.is_a?(String)
736:
                handlerwrapper.inform_activity_manipulate
737:
                if code.is_a?(Proc)
738:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
739:
                  case code.arity
740:
                    when 1; mr.instance_exec(handlerwrapper.activity_result_value,&code)
741:
                    when 2; mr.instance_exec(handlerwrapper.activity_result_value,&code)
742:
                    else
743:
                      mr.instance_exec(&code)
744:
                  end
745:
                elsif code.is_a?(String)
746:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
747:
                  handlerwrapper.manipulate(mr,code,handlerwrapper.activity_result_value)
748:
                end
749:
                handlerwrapper.inform_manipulate_change(
750:
                  (mr.changed_status ? @__weel_status : nil),
751:
                  (mr.changed_data.any? ? mr.changed_data.uniq : nil),
752:
                  (mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil),
753:
                  @__weel_data,
754:
                  @__weel_endpoints
755:
                )
756:
              end
757:
            end while waitingresult == Signal::Again
758:
            if wp.passthrough.nil?
759:
              handlerwrapper.inform_activity_done
760:
              wp.detail = :after
761:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
762:
            end
763:
        end
764:
        raise Signal::Proceed
765:
      rescue Signal::SkipManipulate, Signal::Proceed
766:
        if self.__weel_state != :stopping && self.__weel_state != :finishing && !handlerwrapper.vote_sync_after
767:
          self.__weel_state = :stopping
768:
          wp.detail = :unmark
769:
        end
770:
      rescue Signal::NoLongerNecessary
771:
        @__weel_positions.delete wp
772:
        Thread.current[:branch_position] = nil
773:
        wp.detail = :unmark
774:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :unmark => [wp.position]
775:
      rescue Signal::StopSkipManipulate, Signal::Stop
776:
        self.__weel_state = :stopping
777:
      rescue Signal::Skip
778:
        nil
779:
      rescue SyntaxError => se
780:
        handlerwrapper.inform_activity_failed se
781:
        self.__weel_state = :stopping
782:
      rescue => err
783:
        handlerwrapper.inform_activity_failed err
784:
        self.__weel_state = :stopping
785:
      ensure
786:
        Thread.current[:continue].clear if Thread.current[:continue] && Thread.current[:continue].is_a?(Continue)
787:
      end
788:
    end # }}}
789:
 
790:
    def __weel_recursive_print(thread,indent='')# {{{
791:
      p "#{indent}#{thread}"
792:
      if thread[:branches]
793:
        thread[:branches].each do |b|
794:
          __weel_recursive_print(b,indent+'  ')
795:
        end
796:
      end
797:
    end  # }}}
798:
    def __weel_recursive_continue(thread)# {{{
799:
      return unless thread
800:
      if thread.alive? && thread[:continue]
801:
        thread[:continue].continue
802:
      end
803:
      if thread.alive? && thread[:branch_event]
804:
        thread[:mutex].synchronize do
805:
          thread[:branch_event].continue unless thread[:branch_event].nil?
806:
        end
807:
      end
808:
      if thread[:branches]
809:
        thread[:branches].each do |b|
810:
          __weel_recursive_continue(b)
811:
        end
812:
      end
813:
    end  # }}}
814:
    def __weel_recursive_join(thread)# {{{
815:
      return unless thread
816:
      if thread.alive? && thread != Thread.current
817:
        thread.join
818:
      end
819:
      if thread[:branches]
820:
        thread[:branches].each do |b|
821:
          __weel_recursive_join(b)
822:
        end
823:
      end
824:
    end  # }}}
825:
 
826:
    def __weel_position_test(position)# {{{
827:
      if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
828:
        position
829:
      else
830:
        self.__weel_state = :stopping
831:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("position (#{position}) not valid"),nil)
832:
      end
833:
    end # }}}
834:
 
835:
    def __weel_is_in_search_mode(position = nil)# {{{
836:
      branch = Thread.current
837:
      return false if @__weel_search_positions.empty? || branch[:branch_search] == false
838:
 
839:
      if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
840:
        branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
841:
        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
842:
          branch = branch[:branch_parent]
843:
          branch[:branch_search] = false
844:
        end
845:
        @__weel_search_positions[position].detail == :after ? :after : false
846:
      else
847:
        branch[:branch_search] = true
848:
      end
849:
    end # }}}
850:
 
851:
    def __weel_sim #{{{
852:
      @__weel_state == :simulating
853:
    end #}}}
854:
 
855:
    def __weel_sim_start(what,options={}) #{{{
856:
      current_branch_sim_pos = Thread.current[:branch_sim_pos]
857:
      Thread.current[:branch_sim_pos] = @__weel_sim += 1
858:
      handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
859:
      handlerwrapper.simulate(what,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
860:
      [handlerwrapper, current_branch_sim_pos]
861:
    end #}}}
862:
 
863:
    def __weel_sim_stop(what,handlerwrapper,current_branch_sim_pos,options={}) #{{{
864:
      handlerwrapper.simulate(what,:end,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
865:
      Thread.current[:branch_sim_pos] = current_branch_sim_pos
866:
    end #}}}
867:
 
868:
  public
869:
    def __weel_finalize #{{{
870:
      __weel_recursive_join(@__weel_main)
871:
      @__weel_state = :stopped
872:
    end #}}}
873:
 
874:
    def __weel_state=(newState)# {{{
875:
      return @__weel_state if newState == @__weel_state && @__weel_state != :ready
876:
 
877:
      @__weel_positions = Array.new if newState == :running
878:
      @__weel_state = newState
879:
 
880:
      if newState == :stopping || newState == :finishing
881:
        @__weel_status.nudge!
882:
        __weel_recursive_continue(@__weel_main)
883:
      end
884:
 
885:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
886:
    end # }}}
887:
 
888:
  end # }}}
889:
 
890:
public
891:
  def positions # {{{
892:
    @dslr.__weel_positions
893:
  end # }}}
894:
 
895:
  # set the handlerwrapper
896:
  def handlerwrapper # {{{
897:
    @dslr.__weel_handlerwrapper
898:
  end # }}}
899:
  def handlerwrapper=(new_weel_handlerwrapper) # {{{
900:
    superclass = new_weel_handlerwrapper
901:
    while superclass
902:
      check_ok = true if superclass == WEEL::HandlerWrapperBase
903:
      superclass = superclass.superclass
904:
    end
905:
    raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
906:
    @dslr.__weel_handlerwrapper = new_weel_handlerwrapper
907:
  end # }}}
908:
 
909:
  # Get/Set the handlerwrapper arguments
910:
  def handlerwrapper_args # {{{
911:
    @dslr.__weel_handlerwrapper_args
912:
  end # }}}
913:
  def handlerwrapper_args=(args) # {{{
914:
    if args.class == Array
915:
      @dslr.__weel_handlerwrapper_args = args
916:
    end
917:
    nil
918:
  end #  }}}
919:
 
920:
  # Get the state of execution (ready|running|stopping|stopped|finished|simulating)
921:
  def state # {{{
922:
    @dslr.__weel_state
923:
  end #  }}}
924:
  def state_signal # {{{
925:
    handlerwrapper::inform_state_change handlerwrapper_args, state
926:
    state
927:
  end # }}}
928:
 
929:
  # Set search positions
930:
  # set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
931:
  def search(new_weel_search=false) # {{{
932:
    @dslr.__weel_search_positions.clear
933:
 
934:
    new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
935:
 
936:
    if !new_weel_search.is_a?(Array) || new_weel_search.empty?
937:
      false
938:
    else
939:
      new_weel_search.each do |search_position|
940:
        @dslr.__weel_search_positions[search_position.position] = search_position
941:
      end
942:
      true
943:
    end
944:
  end # }}}
945:
 
946:
  def data(new_data=nil) # {{{
947:
    unless new_data.nil? || !new_data.is_a?(Hash)
948:
      new_data.each{ |k,v| @dslr.__weel_data[k] = v }
949:
    end
950:
    @dslr.__weel_data
951:
  end # }}}
952:
  def endpoints(new_endpoints=nil) # {{{
953:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
954:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
955:
    end
956:
    @dslr.__weel_endpoints
957:
  end # }}}
958:
  def endpoint(new_endpoints) # {{{
959:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
960:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
961:
    end
962:
    nil
963:
  end # }}}
964:
  def status # {{{
965:
    @dslr.__weel_status
966:
  end # }}}
967:
 
968:
  # get/set workflow description
969:
  def description(&blk)
970:
    self.description=(blk)
971:
  end
972:
  def description=(code) # {{{
973:
    (class << self; self; end).class_eval do
974:
      remove_method :__weel_control_flow if method_defined? :__weel_control_flow
975:
      define_method :__weel_control_flow do |state,final_state=:finished|
976:
        @dslr.__weel_positions.clear
977:
        @dslr.__weel_state = state
978:
        begin
979:
          if code.is_a? Proc
980:
            @dslr.instance_eval(&code)
981:
          else
982:
            @dslr.instance_eval(code)
983:
          end
984:
        rescue SyntaxError => se
985:
          @dslr.__weel_state = :stopping
986:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(se.message),code)
987:
        rescue NameError => err # don't look into it, or it will explode
988:
          @dslr.__weel_state = :stopping
989:
          @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)
990:
        rescue => err
991:
          @dslr.__weel_state = :stopping
992:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(err.message),code)
993:
        end
994:
        if @dslr.__weel_state == :running || @dslr.__weel_state == :finishing
995:
          ipc = { :unmark => [] }
996:
          @dslr.__weel_positions.each{ |wp| ipc[:unmark] << wp.position }
997:
          @dslr.__weel_positions.clear
998:
          @dslr.__weel_handlerwrapper::inform_position_change(@dslr.__weel_handlerwrapper_args,ipc)
999:
          @dslr.__weel_state = :finished
1000:
        end
1001:
        if @dslr.__weel_state == :simulating
1002:
          @dslr.__weel_state = final_state
1003:
        end
1004:
        if @dslr.__weel_state == :stopping
1005:
          @dslr.__weel_finalize
1006:
        end
1007:
      end
1008:
    end
1009:
  end # }}}
1010:
 
1011:
  # Stop the workflow execution
1012:
  def stop # {{{
1013:
    Thread.new do
1014:
      @dslr.__weel_state = :stopping
1015:
      @dslr.__weel_main.join if @dslr.__weel_main
1016:
    end
1017:
  end # }}}
1018:
  # Start the workflow execution
1019:
  def start # {{{
1020:
    return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
1021:
    @dslr.__weel_main = Thread.new do
1022:
      begin
1023:
        __weel_control_flow(:running)
1024:
      rescue => e
1025:
        puts e.message
1026:
        puts e.backtrace
1027:
        handlerwrapper::inform_handlerwrapper_error handlerwrapper_args, e
1028:
      end
1029:
    end
1030:
  end # }}}
1031:
 
1032:
  def sim # {{{
1033:
    stat = @dslr.__weel_state
1034:
    return nil unless stat == :ready || stat == :stopped
1035:
    @dslr.__weel_main = Thread.new do
1036:
      __weel_control_flow :simulating, stat
1037:
    end
1038:
  end # }}}
1039:
 
1040:
end