-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutil.rb
More file actions
156 lines (131 loc) · 4.57 KB
/
util.rb
File metadata and controls
156 lines (131 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
75
76
77
78
79
80
81
82
83
84
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# Some Utility modules and classes
#
# Author: Ram (Munagala V. Ramanath)
#
# Copyright 2012 Munagala V. Ramanath.
#
require 'logger'
module Constants # common constants
DEF_QUEUE_SIZE = 5000 # default job queue size
DEF_PORT = 11_111 # default listener port on boss
ACK, QUIT = 'ack', 'quit' # acknowledge, quit tokens
end # Constants
# Wrapper for result of computation: created in worker process and unmarshalled in the
# boss (application)
#
class Result
# success -- true iff computation was successful
# result -- result (object) of computation if success == true (undefined otherwise)
# err_msg -- error message (String) for failed computation if success == false
# (undefined otherwise)
#
attr :success, :err_msg, :result
def initialize s, m, r
@success = s
if s
@result = r
else
@err_msg = m
end
end # initialize
end # Result
class Util
# write message string to socket; used by both boss and workers
def self.send_str s, msg # s is socket, msg is string to send
# logging omitted to minimize overhead; enable if necessary
#log = Log.instance
#log.debug "Thread #{Thread.current[:name]}: sending msg, length = #{msg.size}"
bsize = msg.bytesize # size in bytes (may differ from character size !)
s.puts bsize # write size in characters
nbytes = s.write msg # write string
s.flush
# we expect that Ruby does the necessary looping to complete the write, so this
# exception should never trigger
#
raise "Thread #{Thread.current[:name]}: partial write, #{nbytes} != #{bsize}" if
nbytes != bsize
#log.debug "Thread #{Thread.current[:name]}: waiting for ack"
ack = s.gets
raise "Thread #{Thread.current[:name]}: Got nil instead of ACK" if ack.nil?
ack.strip!
raise "Thread #{Thread.current[:name]}: Bad ACK #{ack}" if Constants::ACK != ack
#log.debug "#{pfx}: Got ack"
end # send_str
# read string data from socket and return it; used by both boss and workers
#
# + read size of data in bytes (sz); if it is QUIT, that constant is returned
# + read sz bytes using multiple reads if necessary
# + send ACK
# + return result
#
def self.recv_str s # arg is socket
# length of string
msg = s.gets
raise "Thread #{Thread.current[:name]}: msg(1): Got nil" if msg.nil?
msg.strip!
return Constants::QUIT if Constants::QUIT == msg
bsize = msg.to_i # size in bytes (may differ from size in chars !)
# read message
data = s.read bsize
raise "Thread #{Thread.current[:name]}: msg(2): Got nil" if data.nil?
while !(len = bsize - data.bytesize).zero? do # need another read
str = s.read len
raise "Thread #{Thread.current[:name]}: (data loop) Got nil" if str.nil?
raise "Thread #{Thread.current[:name]}: (data loop) need #{len} bytes, got 0" if
str.empty?
data += str
end
# send ACK
s.puts Constants::ACK
return data
end # recv_str
end # Util
# single common logger for all classes
class Log < Logger
include Singleton
# default name, maximum size (bytes) and number of log files
F_NAME, F_SIZE, F_COUNT = 'main.log', 4_000_000, 2
# logger details:
#
# f_name -- name prefix for log files
# f_cnt -- no. of log filesfile name prefix
# f_size -- max size of log files
#
@@f_name, @@f_cnt, @@f_size = F_NAME, F_COUNT, F_SIZE
# initialize logger details -- _must_ be called by a single thread before the first
# invocation of the instance() method; throws exception if called after because the
# unique instance has already been created.
#
def self.init( args ) # args is a hash
if defined? @@created # single instance already created
warn "Logger #{@@f_name} already initialized"
return
end
raise "No arguments" if !args || args.empty?
# all parameters optional
name = args[ :name ]
if name
name = name.strip
raise "Empty file name" if name.empty?
@@f_name = name
end
c = args[ :count ] # file count
if c
raise "Bad file count" if (c < 1 || c > 100)
@@f_cnt = c
end
sz = args[ :size ] # file size
if sz
raise "File size too small #{sz}" if sz < 32_000
raise "File size too large #{sz}" if sz > 2**30
@@f_size = sz
end
end # init
def initialize
super @@f_name, @@f_cnt, @@f_size
self.datetime_format = "%H:%M:%S"
# levels are: DEBUG, INFO, WARN, ERROR and FATAL
self.level = Logger::DEBUG
@@created = true
end # initialize
end # Log