Feature #4464 » 0001-add-Fcntl-Flock-object-for-easier-use-of-POSIX-file-.patch
ext/fcntl/extconf.rb | ||
---|---|---|
require 'mkmf'
|
||
have_type('struct flock', %w(unistd.h fcntl.h))
|
||
create_makefile('fcntl')
|
ext/fcntl/fcntl.c | ||
---|---|---|
#include "ruby.h"
|
||
#include <fcntl.h>
|
||
static void Init_FcntlFlock(VALUE);
|
||
/* Fcntl loads the constants defined in the system's <fcntl.h> C header
|
||
* file, and used with both the fcntl(2) and open(2) POSIX system calls.
|
||
... | ... | |
Init_fcntl()
|
||
{
|
||
VALUE mFcntl = rb_define_module("Fcntl");
|
||
Init_FcntlFlock(mFcntl);
|
||
#ifdef F_DUPFD
|
||
rb_define_const(mFcntl, "F_DUPFD", INT2NUM(F_DUPFD));
|
||
#endif
|
||
... | ... | |
rb_define_const(mFcntl, "O_ACCMODE", INT2FIX(O_RDONLY | O_WRONLY | O_RDWR));
|
||
#endif
|
||
}
|
||
#ifdef HAVE_TYPE_STRUCT_FLOCK
|
||
static short
|
||
num2short(VALUE val)
|
||
{
|
||
int i = NUM2INT(val);
|
||
if (i != (short)i) {
|
||
const char *s = i > 0 ? "big" : "small";
|
||
rb_raise(rb_eRangeError, "Integer %d too %s to for `short'", i, s);
|
||
}
|
||
return (short)i;
|
||
}
|
||
static struct flock *
|
||
flock_ptr(VALUE self)
|
||
{
|
||
if (RSTRING_LEN(self) < (long)sizeof(struct flock))
|
||
rb_raise(rb_eRuntimeError, "flock buffer shortened");
|
||
rb_str_modify(self);
|
||
return (struct flock *)RSTRING_PTR(self);
|
||
}
|
||
static VALUE
|
||
flock_init(int argc, VALUE *argv, VALUE self)
|
||
{
|
||
struct flock *flock;
|
||
VALUE type, whence, start, len;
|
||
rb_str_resize(self, (long)sizeof(struct flock));
|
||
flock = flock_ptr(self);
|
||
rb_scan_args(argc, argv, "04", &type, &whence, &start, &len);
|
||
flock->l_type = NIL_P(type) ? F_RDLCK : num2short(type);
|
||
flock->l_whence = NIL_P(whence) ? SEEK_SET : num2short(whence);
|
||
flock->l_start = NIL_P(start) ? 0 : NUM2OFFT(start);
|
||
flock->l_len = NIL_P(len) ? 0 : NUM2OFFT(len);
|
||
flock->l_pid = (pid_t)-1;
|
||
return self;
|
||
}
|
||
static VALUE
|
||
l_type(VALUE self)
|
||
{
|
||
return INT2NUM(flock_ptr(self)->l_type);
|
||
}
|
||
static VALUE
|
||
set_l_type(VALUE self, VALUE val)
|
||
{
|
||
flock_ptr(self)->l_type = num2short(val);
|
||
return val;
|
||
}
|
||
static VALUE
|
||
l_whence(VALUE self)
|
||
{
|
||
return INT2NUM(flock_ptr(self)->l_whence);
|
||
}
|
||
static VALUE
|
||
set_l_whence(VALUE self, VALUE val)
|
||
{
|
||
flock_ptr(self)->l_whence = num2short(val);
|
||
return val;
|
||
}
|
||
static VALUE
|
||
l_start(VALUE self)
|
||
{
|
||
return OFFT2NUM(flock_ptr(self)->l_start);
|
||
}
|
||
static VALUE
|
||
set_l_start(VALUE self, VALUE val)
|
||
{
|
||
flock_ptr(self)->l_start = NUM2OFFT(val);
|
||
return val;
|
||
}
|
||
static VALUE
|
||
l_len(VALUE self)
|
||
{
|
||
return OFFT2NUM(flock_ptr(self)->l_len);
|
||
}
|
||
static VALUE
|
||
set_l_len(VALUE self, VALUE val)
|
||
{
|
||
flock_ptr(self)->l_len = NUM2OFFT(val);
|
||
return val;
|
||
}
|
||
static VALUE
|
||
l_pid(VALUE self)
|
||
{
|
||
return PIDT2NUM(flock_ptr(self)->l_pid);
|
||
}
|
||
static VALUE
|
||
flock_inspect(VALUE self)
|
||
{
|
||
struct flock *flock = flock_ptr(self);
|
||
const char fmt[] = "#<%s: type=%s, whence=%s, start=%lld, len=%lld, pid=%ld>";
|
||
const char *name = rb_obj_classname(self);
|
||
const char *type, *whence;
|
||
switch (flock->l_type) {
|
||
case F_RDLCK: type = "Fcntl::F_RDLCK"; break;
|
||
case F_WRLCK: type = "Fcntl::F_WRLCK"; break;
|
||
case F_UNLCK: type = "Fcntl::F_UNLCK"; break;
|
||
default: type = "(unknown)";
|
||
}
|
||
switch (flock->l_whence) {
|
||
case SEEK_SET: whence = "IO::SEEK_SET"; break;
|
||
case SEEK_CUR: whence = "IO::SEEK_CUR"; break;
|
||
case SEEK_END: whence = "IO::SEEK_END"; break;
|
||
default: whence = "(unknown)";
|
||
}
|
||
return rb_sprintf(fmt, name, type, whence,
|
||
(unsigned long long)flock->l_start,
|
||
(unsigned long long)flock->l_len,
|
||
(long)flock->l_pid);
|
||
}
|
||
static void
|
||
Init_FcntlFlock(VALUE mFcntl)
|
||
{
|
||
VALUE cFcntlFlock = rb_define_class_under(mFcntl, "Flock", rb_cString);
|
||
rb_define_private_method(cFcntlFlock, "initialize", flock_init, -1);
|
||
rb_define_method(cFcntlFlock, "type", l_type, 0);
|
||
rb_define_method(cFcntlFlock, "type=", set_l_type, 1);
|
||
rb_define_method(cFcntlFlock, "whence", l_whence, 0);
|
||
rb_define_method(cFcntlFlock, "whence=", set_l_whence, 1);
|
||
rb_define_method(cFcntlFlock, "start", l_start, 0);
|
||
rb_define_method(cFcntlFlock, "start=", set_l_start, 1);
|
||
rb_define_method(cFcntlFlock, "len", l_len, 0);
|
||
rb_define_method(cFcntlFlock, "len=", set_l_len, 1);
|
||
rb_define_method(cFcntlFlock, "pid", l_pid, 0);
|
||
rb_define_method(cFcntlFlock, "inspect", flock_inspect, 0);
|
||
}
|
||
#else /* ! HAVE_TYPE_STRUCT_FLOCK */
|
||
static void Init_FcntlFlock(VALUE mFcntl) { }
|
||
#endif /* HAVE_TYPE_STRUCT_FLOCK */
|
test/fcntl/test_fcntl_flock.rb | ||
---|---|---|
require "test/unit"
|
||
begin
|
||
require "fcntl"
|
||
rescue LoadError
|
||
end
|
||
require "tempfile"
|
||
class TestFcntlFlock < Test::Unit::TestCase
|
||
def test_initialize_noargs
|
||
flock = Fcntl::Flock.new
|
||
assert_equal Fcntl::F_RDLCK, flock.type
|
||
assert_equal IO::SEEK_SET, flock.whence
|
||
assert_equal 0, flock.start
|
||
assert_equal 0, flock.len
|
||
end
|
||
def test_inspect
|
||
expect = "#<Fcntl::Flock: type=Fcntl::F_RDLCK, whence=IO::SEEK_SET, " \
|
||
"start=0, len=0, pid=-1>"
|
||
assert_equal expect, Fcntl::Flock.new.inspect
|
||
end
|
||
def test_initialize_write_lock
|
||
flock = Fcntl::Flock.new Fcntl::F_WRLCK
|
||
assert_equal Fcntl::F_WRLCK, flock.type
|
||
assert_equal IO::SEEK_SET, flock.whence
|
||
assert_equal 0, flock.start
|
||
assert_equal 0, flock.len
|
||
end
|
||
def test_accessors
|
||
flock = Fcntl::Flock.new
|
||
assert_kind_of Integer, flock.pid
|
||
flock.type = Fcntl::F_UNLCK
|
||
assert_equal Fcntl::F_UNLCK, flock.type
|
||
flock.whence = IO::SEEK_CUR
|
||
assert_equal IO::SEEK_CUR, flock.whence
|
||
flock.len = 2
|
||
assert_equal 2, flock.len
|
||
flock.start = 3
|
||
assert_equal 3, flock.start
|
||
end
|
||
def test_get_lock
|
||
lock = Fcntl::Flock.new Fcntl::F_WRLCK
|
||
pid = nil
|
||
Tempfile.open(self.class.name) do |tmp|
|
||
r, w = IO.pipe
|
||
pid = fork do
|
||
File.open(tmp.path, "wb") do |fp|
|
||
fp.fcntl Fcntl::F_SETLKW, lock
|
||
r.close
|
||
w.syswrite "."
|
||
sleep
|
||
end
|
||
end
|
||
w.close
|
||
assert_equal ".", r.read(1)
|
||
r.close
|
||
tmp.fcntl Fcntl::F_GETLK, lock
|
||
end
|
||
assert_equal pid, lock.pid
|
||
Process.kill :TERM, pid
|
||
Process.waitpid2(pid)
|
||
end
|
||
end if defined? Fcntl::Flock
|