Class: Fairy::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/fairy/controller.rb

Defined Under Namespace

Classes: Context, MPInputNewProcessor, MPInputNewProcessorN, MPInputProcessor, MPLocalInputNewProcessorN, MPNewProcessor, MPNewProcessorN, MPSameNTask, MPSameProcessor, MPVarrayInputProcessor, NjobMapper, NjobMappingPolicy

Constant Summary collapse

EXPORTS =
[]
MPSameProcessorQ =
MPSameProcessor

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id) ⇒ Controller

Returns a new instance of Controller.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/fairy/controller.rb', line 42

def initialize(id)
  @id = id

  @deepconnect = nil

  @master_deepspace = nil
  @master = nil

  @client = nil

  @stdout_mutex = Mutex.new

  @services = {}

  @create_processor_mutex = Mutex.new

  # processor -> no of reserve 
  @reserves = {}
  @reserves_mutex = Mutex.new
  @reserves_cv = ConditionVariable.new

  # bjob -> [processor, ...]
  @bjob2processors = {}
  @bjob2processors_mutex = Mutex.new
  @bjob2processors_cv = ConditionVariable.new

  # processor -> no of active ntasks
  @no_active_ntasks = {}
  @no_active_ntasks_mutex = Mutex.new
  @no_active_ntasks_cv = ConditionVariable.new

  @pool_dict = PoolDictionary.new
end

Instance Attribute Details

#create_processor_mutexObject (readonly)

Returns the value of attribute create_processor_mutex.



77
78
79
# File 'lib/fairy/controller.rb', line 77

def create_processor_mutex
  @create_processor_mutex
end

#hash_seedObject (readonly)

Returns the value of attribute hash_seed.



79
80
81
# File 'lib/fairy/controller.rb', line 79

def hash_seed
  @hash_seed
end

#idObject (readonly)

Returns the value of attribute id.



76
77
78
# File 'lib/fairy/controller.rb', line 76

def id
  @id
end

Class Method Details

.def_export(obj, name = nil) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/fairy/controller.rb', line 21

def Controller.def_export(obj, name = nil)
  unless name
	if obj.kind_of?(Class)
	  if /Fairy::(.*)$/ =~ obj.name
 name = $1
	  else
 name = obj.name
	  end
	else
	  ERR::Raise ERR::INTERNAL::CantDefExport, obj.to_s
	end
  end

  EXPORTS.push [name, obj]
end

.start(id, master_port) ⇒ Object



37
38
39
40
# File 'lib/fairy/controller.rb', line 37

def Controller.start(id, master_port)
  controller = Controller.new(id)
  controller.start(master_port)
end

Instance Method Details

#assign_input_processor(bjob, host, &block) ⇒ Object

methods of assgin processor.



410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/fairy/controller.rb', line 410

def assign_input_processor(bjob, host, &block)
  node = @master.node_in_reisured(host)
  unless node
	begin
	  ERR::Raise ERR::NodeNotArrived, host 
	rescue
	  handle_exception($!)
	  raise AbortCreateNode
	end
  end
  create_processor(node, bjob, &block)
end

#assign_input_processor_n(bjob, host, &block) ⇒ Object



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/fairy/controller.rb', line 423

def assign_input_processor_n(bjob, host, &block)
  max_no = CONF.CONTROLLER_INPUT_PROCESSOR_N
  max_ntasks = CONF.CONTROLLER_MAX_ACTIVE_TASKS_IN_PROCESSOR

  loop do
	node = @master.node_in_reisured(host)
	unless node
	  begin
 ERR::Raise ERR::NodeNotArrived, host 
	  rescue
 handle_exception($!)
 raise AbortCreateNode
	  end
	end

	no_of_processors = 0
	leisured_processor = nil
	min = nil
	for processor in @bjob2processors[bjob].dup
	  next if processor.node != node
	  no_of_processors += 1
	  
	  n = no_active_ntasks_in_processor(processor)
	  if !min or min > n
 min = n
 leisured_processor = processor
	  end
	end

	if max_no.nil? || max_no >= no_of_processors
	  create_processor(node, bjob, &block)
	  return
	elsif min > max_ntasks
	  @no_active_ntasks_mutex.synchronize do
 Log::debug(self, "NO_ACTIVE_NTASKS: WAIT")
 @no_active_ntasks_cv.wait(@no_active_ntasks_mutex)
 Log::debug(self, "NO_ACTIVE_NTASKS: WAIT END")
	  end
	else
	  ret = reserve_processor(leisured_processor) {|processor|
 register_processor(bjob, processor)
 yield processor
	  }
	  unless ret
 # プロセッサが終了していたとき. もうちょっとどうにかしたい気もする
 assign_new_processor(bjob, &block)
	  end
	  return
	end
  end
end

#assign_new_processor(bjob, &block) ⇒ Object



549
550
551
552
# File 'lib/fairy/controller.rb', line 549

def assign_new_processor(bjob, &block)
  node = @master.leisured_node
  create_processor(node, bjob, &block)
end

#assign_new_processor_n(bjob, input_bjob, &block) ⇒ Object

まあ, 大体n個になるかなぁ… input_bjobのプロセスも動的に割り当てられるので… 最終的には 大体そうなるということで.…



557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
# File 'lib/fairy/controller.rb', line 557

def assign_new_processor_n(bjob, input_bjob, &block)

  if input_bjob
	factor = CONF.CONTROLLER_ASSIGN_NEW_PROCESSOR_N_FACTOR
  else
	# ここバグっている? CONTROLLER_INPUT_PROCESSOR_N は1-node辺りの数
	max_no = CONF.CONTROLLER_INPUT_PROCESSOR_N
  end
  max_ntasks = CONF.CONTROLLER_MAX_ACTIVE_TASKS_IN_PROCESSOR

  loop do
	if input_bjob
	  no_i = 0
	  @bjob2processors_mutex.synchronize do
 while !@bjob2processors[input_bjob]
   Log::debug(self, "ASSIGN NEW PROCESSOR WAIT: #{bjob.class}")
   @bjob2processors_cv.wait(@bjob2processors_mutex)
   Log::debug(self, "ASSIGN NEW PROCESSOR RESUME: #{bjob.class}")
 end
 if i_processors = @bjob2processors[input_bjob]
   no_i += i_processors.size
 end
	  end
	  max_no = no_i * factor
	end

	no = 0
	if processors = @bjob2processors[bjob]
	  no += processors.size
	end

	if max_no > no
	  node = @master.leisured_node
	  create_processor(node, bjob, &block)
	  return
	else
	  leisured_processor = nil
	  min = nil
	  for processor in @bjob2processors[bjob].dup
 # これだと頭から割り当てられる... 
 # けど取りあえずということで.
 
 n = no_active_ntasks_in_processor(processor)
 if !min or min > n
   min = n
   leisured_processor = processor
 end
	  end

	  if min > max_ntasks
 @no_active_ntasks_mutex.synchronize do
   Log::debug(self, "NO_ACTIVE_NTASKS: WAIT")
   @no_active_ntasks_cv.wait(@no_active_ntasks_mutex)
   Log::debug(self, "NO_ACTIVE_NTASKS: WAIT END")
 end
	  else
 ret = reserve_processor(leisured_processor) {|processor|
   register_processor(bjob, processor)
   yield processor
 }
 unless ret
   # プロセッサが終了していたとき. もうちょっとどうにかしたい気もする
   assign_new_processor(bjob, &block)
 end
 return
	  end
	end
  end
end

#assign_new_processor_n_for_local_io(bjob, &block) ⇒ Object



627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
# File 'lib/fairy/controller.rb', line 627

def assign_new_processor_n_for_local_io(bjob, &block)

  nodes = {}
#      for p in @bjob2processors[bjob].dup
  for p in bjob.nodes.collect{|njob| njob.processor}
	if nodes[p.node]
	  nodes[p.node].push p
	else
	  nodes[p.node] = [p]
	end
  end

  node = nil
  assign_level = 0
  while !node
	assign_level += 1
	except_nodes = nodes.select{|n, ps| ps.size >= assign_level}
	node = @master.leisured_node_except_nodes(except_nodes, false)
  end

  max_no = CONF.CONTROLLER_INPUT_PROCESSOR_N
  if nodes[node]
	leisured_processor = nil
	min = nil
	for processor in nodes[node]
	  n = processor.no_ntasks
	  if !min or min > n
 min = n
 leisured_processor = processor
	  end
	end
	no_of_processors = nodes[node].size
  else
	no_of_processors = 0
  end

  if max_no.nil? || max_no >= no_of_processors
	create_processor(node, bjob, &block)
  else
	ret = reserve_processor(leisured_processor) {|processor|
	  register_processor(bjob, processor)
	  yield processor
	}
	unless ret
	  # プロセッサが終了していたとき. もうちょっとどうにかしたい気もする
	  assign_new_processor(bjob, &block)
	end
  end
end

#assign_ntasks(target_bjob, create_node_mutex, &block) ⇒ Object

def assign_processor(target_bjob, &block)

  mapper = NjobMapper.new(self, target_bjob)
  mapper.assign_processor(&block)
end


780
781
782
783
784
785
786
787
# File 'lib/fairy/controller.rb', line 780

def assign_ntasks(target_bjob, create_node_mutex, &block)
  target_bjob.input.each_assigned_filter do |input_filter|
	mapper = NjobMapper.new(self, target_bjob, input_filter)
#	create_node_mutex.synchronize do
	  mapper.assign_ntask(&block)
#	end
  end
end

#assign_same_obj_processor(bjob, obj, &block) ⇒ Object



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
# File 'lib/fairy/controller.rb', line 529

def assign_same_obj_processor(bjob, obj, &block)
  processor = nil
  @reserves_mutex.synchronize do
	@reserves.each_key do |p| 
	  if p.deep_space == obj.deep_space
 processor = p
 break
	  end
	end
  end
  ERR::Raise ERR::NoExistProcesorWithObject obj.to_s unless processor

  ret = reserve_processor(processor) {
	register_processor(bjob, processor)
	yield processor
  }
  
  ERR::Raise ERR::NoExistProcesorWithObject obj.to_s unless ret
end

#assign_same_processor(bjob, processor, &block) ⇒ Object



515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/fairy/controller.rb', line 515

def assign_same_processor(bjob, processor, &block)
  # このメソッドは, 基本的にはreserve しているだけ
  ret = reserve_processor(processor) {|processor|
	register_processor(bjob, processor)
	yield processor
	processor}

  unless ret
	# プロセッサが終了していたとき(ほとんどあり得ないけど)
	# この時のassgin_processor側の処理がイマイチ
	assign_new_processor(bjob, &block)
  end
end

#connect(client, conf) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/fairy/controller.rb', line 115

def connect(client, conf)
  @client = client
  
  conf.base_conf = CONF
  Fairy::REPLACE_CONF(conf)
  
  mod = CONF.GROUP_BY_HASH_MODULE
  require mod
  @hash_seed = Fairy::HValueGenerator.create_seed
  def_pool_variable(:HASH_SEED, @hash_seed)

  @PROCESS_LIFE_MANAGE_INTERVAL = CONF.PROCESS_LIFE_MANAGE_INTERVAL

  if @PROCESS_LIFE_MANAGE_INTERVAL
	Thread.start do
	  start_process_life_manage
	end
	nil
  end

  $stdout = Stdout.new(@client)

end

#create_processor(node, bjob, &block) ⇒ Object



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/fairy/controller.rb', line 340

def create_processor(node, bjob, &block)
  @create_processor_mutex.synchronize do
	processor = node.create_processor
	processor.connect_controller(self, CONF)
	@reserves_mutex.synchronize do
	  @reserves[processor] = 1
	end
	begin
	  register_processor(bjob, processor)
	  yield processor
	  processor
	ensure
	  @reserves_mutex.synchronize do
 @reserves[processor] -= 1
	  end
	end
  end
end

#def_pool_variable(vname, value = nil) ⇒ Object



740
741
742
743
744
745
746
747
# File 'lib/fairy/controller.rb', line 740

def def_pool_variable(vname, value = nil)
  # value が Hash で キー :block をもっていたら block と見なす.
  if value.__deep_connect_reference? && value.kind_of?(Hash) && value[:block]
	p = Context.create_proc(self, value[:block])
	value = p.call 
  end
  @pool_dict.def_variable(vname, value)
end

#export(service, obj) ⇒ Object

clent interface



282
283
284
# File 'lib/fairy/controller.rb', line 282

def export(service, obj)
  @services[service] = obj
end

#handle_exception(exp) ⇒ Object

exception handling



718
719
720
721
722
723
724
725
726
# File 'lib/fairy/controller.rb', line 718

def handle_exception(exp)
  Thread.start do
	begin
	  @client.handle_exception(exp)
	rescue
	end
  end
  nil
end

#import(service) ⇒ Object



286
287
288
# File 'lib/fairy/controller.rb', line 286

def import(service)
  @services[service]
end

#log_idObject



81
82
83
# File 'lib/fairy/controller.rb', line 81

def log_id
  "Controller[#{id}]"
end

#no_active_ntasks_in_processor(processor) ⇒ Object

ntask methods



302
303
304
305
306
# File 'lib/fairy/controller.rb', line 302

def no_active_ntasks_in_processor(processor)
  @no_active_ntasks_mutex.synchronize do
	@no_active_ntasks[processor] || 0
  end
end

#pool_dictObject

pool variable



736
737
738
# File 'lib/fairy/controller.rb', line 736

def pool_dict
  @pool_dict
end

#pool_variable(vname, *value) ⇒ Object



749
750
751
752
753
754
755
# File 'lib/fairy/controller.rb', line 749

def pool_variable(vname, *value)
  if value.empty?
	@pool_dict[vname]
  else
	@pool_dict[vname] = value.first
  end
end

#register_bjob(bjob) ⇒ Object

bjob methods



293
294
295
296
297
# File 'lib/fairy/controller.rb', line 293

def register_bjob(bjob)
  @bjob2processors_mutex.synchronize do
	@bjob2processors[bjob] = []
  end
end

#register_processor(bjob, processor) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
# File 'lib/fairy/controller.rb', line 359

def register_processor(bjob, processor)
  @bjob2processors_mutex.synchronize do
	@bjob2processors[bjob] = [] unless @bjob2processors[bjob]
	unless @bjob2processors[bjob].include?(processor)
	  @bjob2processors[bjob].push processor
	end
	@bjob2processors_cv.broadcast
	@no_active_ntasks_cv.broadcast
  end
  processor
end

#reserve_processor(processor, &block) ⇒ Object

processor methods

reserve してから njob 割り当てを行う



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/fairy/controller.rb', line 320

def reserve_processor(processor, &block)
  @reserves_mutex.synchronize do
	begin
	  return nil unless @reserves[processor]
	rescue DeepConnect::SessionServiceStopped
	  # processor は 終了している可能性がある
	  return nil
	end
	@reserves[processor] += 1
  end
  begin
	yield processor
	processor
  ensure
	@reserves_mutex.synchronize do
	  @reserves[processor] -= 1
	end
  end
end

#start(master_port, service = 0) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/fairy/controller.rb', line 85

def start(master_port, service=0)
  @deepconnect = DeepConnect.start(service)
  @deepconnect.export("Controller", self)

  @deepconnect.when_disconnected do |deepspace, opts|
	when_disconnected(deepspace, opts)
  end

  for name, obj in EXPORTS
	export(name, obj)
  end

  require "fairy/share/inspector"
  @deepconnect.export("Inspector", Inspector.new(self))

  require "fairy/share/log"
  @master_deepspace = @deepconnect.open_deepspace("localhost", master_port)
  @master = @master_deepspace.import("Master")
  @logger = @master.logger
  Log.type = "[C]"
  Log.pid = id
  Log.logger = @logger
  Log::info(self, "Controller Service Start")
  Log::info(self, "\tfairy version: #{Version}")
  Log::info(self, "\t[Powered by #{RUBY_DESCRIPTION}") 

  @master.register_controller(self)

end

#start_process_life_manageObject



684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
# File 'lib/fairy/controller.rb', line 684

def start_process_life_manage
  loop do
	sleep PROCESS_LIFE_MANAGE_INTERVAL
	Log::debug(self, "START_PROCESS_LIFE_MANAGE: S")
	processors = @reserves_mutex.synchronize{@reserves.keys}
	for p in processors
Log::debugf(self, "START_PROCESS_LIFE_MANAGE: 1 %{p}")
	  kill = false
	  @reserves_mutex.synchronize do
#  	    for q, r in @reserves
#  	      puts "#{q.id} =>#{r}"
#  	    end
Log::debug(self, "START_PROCESS_LIFE_MANAGE: 2 ")
 if @reserves[p] == 0 && p.life_out_life_span?
   Log::info self, "Kill #{p.inspectx}"
   kill = true
   @reserves.delete(p)
   @bjob2processors_mutex.synchronize do
		# @bjob2processors から p を削除する必要あるか?
   end
 end
	  end
 Log::debug(self, "START_PROCESS_LIFE_MANAGE: 3 ")
	  if kill
 Log::debug(self, "START_PROCESS_LIFE_MANAGE: 4 ")
 p.node.terminate_processor(p)
	  end
 Log::debug(self, "START_PROCESS_LIFE_MANAGE: 5 ")
	end
 Log::debug(self, "START_PROCESS_LIFE_MANAGE: E ")
  end
end

#stdout_write(str) ⇒ Object

stdout



729
730
731
732
733
# File 'lib/fairy/controller.rb', line 729

def stdout_write(str)
  $stdout.replace_stdout do
	$stdout.write(str)
  end
end

#terminateObject



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/fairy/controller.rb', line 140

def terminate
  # clientが終了したときの終了処理
  # master から呼ばれる

Log::debug(self, "TERMINATE: #1")
# デッドロックするのでNG
#      @reserves_mutex.synchronize do
  @bjob2processors.keys.each do |bjob|
	begin
Log::debug(self, "TERMINATE: #1.1")
	  bjob.abort_create_node
	rescue
Log::debug(self, "TERMINATE: #1.1.1")
	  Log::debug_exception($!)
	end
  end
#      end

Log::debug(self, "TERMINATE: #2")
  cond = true
  while cond
Log::debug(self, "TERMINATE: #2.1")
	@reserves_mutex.synchronize do
Log::debug(self, "TERMINATE: #2.2")
	  cond = false if @reserves.empty?
	  @reserves.keys.each do |p|
Log::debug(self, "TERMINATE: #2.3")
 if @reserves[p] == 0 
Log::debug(self, "TERMINATE: #2.4")
   begin
		p.terminate_all_ntasks
   rescue
Log::debug(self, "TERMINATE: #2.4.1")
		Log::debug_exception($!)
   end
Log::debug(self, "TERMINATE: #2.5")
   begin
		@reserves.delete(p)
   rescue
Log::debug(self, "TERMINATE: #2.5.1")
		Log::debug_exception($!)
   end
Log::debug(self, "TERMINATE: #2.5.1")
   begin
Log::debug(self, "TERMINATE: #2.5.2")
		p.node.terminate_processor(p)
Log::debug(self, "TERMINATE: #2.5.3")
   rescue
Log::debug(self, "TERMINATE: #2.5.4")
		Log::debug_exception($!)
   end
Log::debug(self, "TERMINATE: #2.6")
 end
	  end
Log::debug(self, "TERMINATE: #2.7")
	end
Log::debug(self, "TERMINATE: #2.8")
  end

Log::debug(self, "TERMINATE: #3")
  @reserves.keys.each do |p| 
	begin
	  p.node.terminate_processor(p)
	rescue
#	  p $!, $@
	end
  end

Log::debug(self, "TERMINATE: #4")
  Thread.start do
	sleep 0.2
	begin
	  @deepconnect.stop
	ensure
	  Process.exit!(0)
	end
  end
Log::debug(self, "TERMINATE: #5")
  nil
end

#terminate_processorObject



677
678
679
680
681
682
# File 'lib/fairy/controller.rb', line 677

def terminate_processor
  deresister_processor(processor)
  @master.deregister_processor(processor)
  @node.deregister_processor(processor)
  @node.terminate_processor
end

#terminate_rev0Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/fairy/controller.rb', line 221

def terminate_rev0
  # clientが終了したときの終了処理
  # master から呼ばれる

Log::debug(self, "TERMINATE: #1")
  @reserves_mutex.synchronize do
	@bjob2processors.keys.each do |bjob|
	  bjob.abort_create_node
	end
  end

Log::debug(self, "TERMINATE: #2")
  @reserves.keys.each do |p| 
	begin
Log::debug(self, "TERMINATE: #2.1")
	  p.terminate_all_njobs
Log::debug(self, "TERMINATE: #2.2")
	rescue
	  LOG::debug_exception(self)
	end
  end

Log::debug(self, "TERMINATE: #2.5")
  @reserves.keys.each do |p| 
	begin
	  p.terminate_all_njobs
	rescue
	  LOG::debug_exception(self)
	end
  end

Log::debug(self, "TERMINATE: #3")
  @reserves.keys.each do |p| 
	begin
	  p.node.terminate_processor(p)
	rescue
#	  p $!, $@
	end
  end

Log::debug(self, "TERMINATE: #4")
  Thread.start do
	sleep 0.1
	@deepconnect.stop
	Process.exit(0)
  end
Log::debug(self, "TERMINATE: #5")
  nil
end

#update_active_ntasks(processor, no_active_ntasks) ⇒ Object



308
309
310
311
312
313
314
# File 'lib/fairy/controller.rb', line 308

def update_active_ntasks(processor, no_active_ntasks)
Log::debug(self, "Processor[#{processor.id}] => #{no_active_ntasks}")
  @no_active_ntasks_mutex.synchronize do
	@no_active_ntasks[processor] = no_active_ntasks
	@no_active_ntasks_cv.broadcast
  end
end

#when_disconnected(deepspace, opts) ⇒ Object



271
272
273
274
275
276
277
# File 'lib/fairy/controller.rb', line 271

def when_disconnected(deepspace, opts)
  if deepspace == @client.deep_space
	Log::info(self, "CONTROLLER: client disconnected: Start termination")
	# クライアントがおなくなりになったら, こっちも死ぬよ
	@master.terminate_controller(self)
  end
end