1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/webrick/utils.rb
shyouhei f2a91397fd Add uplevel keyword to Kernel#warn and use it
If uplevel keyword is given, the warning message is prepended
with caller file and line information and the string "warning: ".
The use of the uplevel keyword makes Kernel#warn format output
similar to how rb_warn formats output.

This patch modifies net/ftp and net/imap to use Kernel#warn
instead of $stderr.puts or $stderr.printf, since they are used
for printing warnings.

This makes lib/cgi/core and tempfile use $stderr.puts instead of
warn for debug logging, since they are used for debug printing
and not for warning.

This does not modify bundler, rubygems, or rdoc, as those are
maintained outside of ruby and probably wish to remain backwards
compatible with older ruby versions.

rb_warn_m code is originally from nobu, but I've changed it
so that it only includes the path and lineno from uplevel
(not the method), and also prepends the string "warning: ",
to make it more similar to rb_warn.

From: Jeremy Evans code@jeremyevans.net
Signed-off-by: Urabe Shyouhei shyouhei@ruby-lang.org


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61155 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-12-12 11:56:25 +00:00

270 lines
6.9 KiB
Ruby

# frozen_string_literal: false
#
# utils.rb -- Miscellaneous utilities
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
require 'socket'
require 'io/nonblock'
require 'etc'
module WEBrick
module Utils
##
# Sets IO operations on +io+ to be non-blocking
def set_non_blocking(io)
io.nonblock = true if io.respond_to?(:nonblock=)
end
module_function :set_non_blocking
##
# Sets the close on exec flag for +io+
def set_close_on_exec(io)
io.close_on_exec = true if io.respond_to?(:close_on_exec=)
end
module_function :set_close_on_exec
##
# Changes the process's uid and gid to the ones of +user+
def su(user)
if pw = Etc.getpwnam(user)
Process::initgroups(user, pw.gid)
Process::Sys::setgid(pw.gid)
Process::Sys::setuid(pw.uid)
else
warn("WEBrick::Utils::su doesn't work on this platform", uplevel: 1)
end
end
module_function :su
##
# The server hostname
def getservername
host = Socket::gethostname
begin
Socket::gethostbyname(host)[0]
rescue
host
end
end
module_function :getservername
##
# Creates TCP server sockets bound to +address+:+port+ and returns them.
#
# It will create IPV4 and IPV6 sockets on all interfaces.
def create_listeners(address, port)
unless port
raise ArgumentError, "must specify port"
end
sockets = Socket.tcp_server_sockets(address, port)
sockets = sockets.map {|s|
s.autoclose = false
ts = TCPServer.for_fd(s.fileno)
s.close
ts
}
return sockets
end
module_function :create_listeners
##
# Characters used to generate random strings
RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789" +
"abcdefghijklmnopqrstuvwxyz"
##
# Generates a random string of length +len+
def random_string(len)
rand_max = RAND_CHARS.bytesize
ret = ""
len.times{ ret << RAND_CHARS[rand(rand_max)] }
ret
end
module_function :random_string
###########
require "timeout"
require "singleton"
##
# Class used to manage timeout handlers across multiple threads.
#
# Timeout handlers should be managed by using the class methods which are
# synchronized.
#
# id = TimeoutHandler.register(10, Timeout::Error)
# begin
# sleep 20
# puts 'foo'
# ensure
# TimeoutHandler.cancel(id)
# end
#
# will raise Timeout::Error
#
# id = TimeoutHandler.register(10, Timeout::Error)
# begin
# sleep 5
# puts 'foo'
# ensure
# TimeoutHandler.cancel(id)
# end
#
# will print 'foo'
#
class TimeoutHandler
include Singleton
##
# Mutex used to synchronize access across threads
TimeoutMutex = Thread::Mutex.new # :nodoc:
##
# Registers a new timeout handler
#
# +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed
def TimeoutHandler.register(seconds, exception)
at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
instance.register(Thread.current, at, exception)
end
##
# Cancels the timeout handler +id+
def TimeoutHandler.cancel(id)
instance.cancel(Thread.current, id)
end
def self.terminate
instance.terminate
end
##
# Creates a new TimeoutHandler. You should use ::register and ::cancel
# instead of creating the timeout handler directly.
def initialize
TimeoutMutex.synchronize{
@timeout_info = Hash.new
}
@queue = Thread::Queue.new
@watcher = nil
end
# :nodoc:
private \
def watch
to_interrupt = []
while true
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
wakeup = nil
to_interrupt.clear
TimeoutMutex.synchronize{
@timeout_info.each {|thread, ary|
next unless ary
ary.each{|info|
time, exception = *info
if time < now
to_interrupt.push [thread, info.object_id, exception]
elsif !wakeup || time < wakeup
wakeup = time
end
}
}
}
to_interrupt.each {|arg| interrupt(*arg)}
if !wakeup
@queue.pop
elsif (wakeup -= now) > 0
begin
(th = Thread.start {@queue.pop}).join(wakeup)
ensure
th&.kill&.join
end
end
@queue.clear
end
end
# :nodoc:
private \
def watcher
(w = @watcher)&.alive? and return w # usual case
TimeoutMutex.synchronize{
(w = @watcher)&.alive? and next w # pathological check
@watcher = Thread.start(&method(:watch))
}
end
##
# Interrupts the timeout handler +id+ and raises +exception+
def interrupt(thread, id, exception)
if cancel(thread, id) && thread.alive?
thread.raise(exception, "execution timeout")
end
end
##
# Registers a new timeout handler
#
# +time+:: Timeout in seconds
# +exception+:: Exception to raise when timeout elapsed
def register(thread, time, exception)
info = nil
TimeoutMutex.synchronize{
(@timeout_info[thread] ||= []) << (info = [time, exception])
}
@queue.push nil
watcher
return info.object_id
end
##
# Cancels the timeout handler +id+
def cancel(thread, id)
TimeoutMutex.synchronize{
if ary = @timeout_info[thread]
ary.delete_if{|info| info.object_id == id }
if ary.empty?
@timeout_info.delete(thread)
end
return true
end
return false
}
end
##
def terminate
TimeoutMutex.synchronize{
@timeout_info.clear
@watcher&.kill&.join
}
end
end
##
# Executes the passed block and raises +exception+ if execution takes more
# than +seconds+.
#
# If +seconds+ is zero or nil, simply executes the block
def timeout(seconds, exception=Timeout::Error)
return yield if seconds.nil? or seconds.zero?
# raise ThreadError, "timeout within critical session" if Thread.critical
id = TimeoutHandler.register(seconds, exception)
begin
yield(seconds)
ensure
TimeoutHandler.cancel(id)
end
end
module_function :timeout
end
end