Skip to content

Commit

Permalink
first try to add finalizer support for C-extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
chuckremes committed Feb 25, 2016
1 parent 94ff89a commit aafdd94
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 20 deletions.
45 changes: 26 additions & 19 deletions core/io.rb
Expand Up @@ -80,26 +80,33 @@ class EINPROGRESSWaitWritable < Errno::EINPROGRESS
end

class FileDescriptor
class RIOStream
def self.close(io, raise_exception)
Rubinius.primitive :rio_close
raise PrimitiveFailure, "IO::FileDescriptor::RIOStream.close primitive failed"
end
end

attr_reader :offset

def self.choose_type(fd)
def self.choose_type(fd, io)
stat = File::Stat.fstat(fd)

case stat.ftype
when "file"
BufferedFileDescriptor.new(fd, stat)
BufferedFileDescriptor.new(fd, stat, io)
when "fifo", "characterSpecial"
FIFOFileDescriptor.new(fd, stat)
FIFOFileDescriptor.new(fd, stat, io)
when "socket"
SocketFileDescriptor.new(fd, stat)
SocketFileDescriptor.new(fd, stat, io)
when "directory"
DirectoryFileDescriptor.new(fd, stat)
DirectoryFileDescriptor.new(fd, stat, io)
when "blockSpecial"
raise "cannot make block special"
when "link"
raise "cannot make link"
else
new(fd, stat)
new(fd, stat, io)
end
end

Expand Down Expand Up @@ -188,8 +195,8 @@ def self.set_flag(flag, fd)
end


def initialize(fd, stat)
@descriptor, @stat = fd, stat
def initialize(fd, stat, io)
@descriptor, @stat, @io = fd, stat, io
acc_mode = FileDescriptor.get_flags(@descriptor)

if acc_mode < 0
Expand Down Expand Up @@ -372,6 +379,7 @@ def close
fd = @descriptor

if fd != -1
RIOStream.close(@io, true)
ret_code = FFI::Platform::POSIX.close(fd)

if FFI.call_failed?(ret_code)
Expand Down Expand Up @@ -599,8 +607,7 @@ def finalizer
# Take care of any IO cleanup for the C API here. An IO may
# have been promoted to a low-level RIO struct using #fdopen,
# so we MUST use #fclose to clost it.

# no op for now
return if RIOStream.close(@io, false)

# Ignore any return code... don't care if it fails
FFI::Platform::POSIX.close(fd) if @autoclose
Expand Down Expand Up @@ -765,8 +772,8 @@ def self.connect_pipe_fds
return [fd0, fd1]
end

def initialize(fd, stat, mode=nil)
super(fd, stat)
def initialize(fd, stat, io, mode=nil)
super(fd, stat, self)
@mode = mode if mode
@eof = false # force to false
end
Expand Down Expand Up @@ -804,7 +811,7 @@ class DirectoryFileDescriptor < BufferedFileDescriptor
end # class DirectoryFileDescriptor

class SocketFileDescriptor < FIFOFileDescriptor
def initialize(fd, stat)
def initialize(fd, stat, io)
super

@mode &= O_ACCMODE
Expand Down Expand Up @@ -1699,7 +1706,7 @@ def self.setup(io, fd, mode=nil, sync=false)
# allocate another because we could double up on finalizers for the same
# fd. Only allocate one here if +fd+ ivar is nil.
if io.instance_variable_get(:@fd).nil?
fd_obj = FileDescriptor.choose_type(fd)
fd_obj = FileDescriptor.choose_type(fd, io)
io.instance_variable_set(:@fd, fd_obj)
end
io.mode = mode || cur_mode
Expand Down Expand Up @@ -1729,7 +1736,7 @@ def initialize(fd, mode=undefined, options=undefined)
mode, binary, external, internal, @autoclose = IO.normalize_options(mode, options)

fd = Rubinius::Type.coerce_to fd, Integer, :to_int
@fd = FileDescriptor.choose_type(fd)
@fd = FileDescriptor.choose_type(fd, self)
raise "FD could not be allocated for fd [#{fd}]" unless @fd
raise "No descriptor set for fd [#{fd}]" unless @fd.descriptor
autoclose = @autoclose
Expand Down Expand Up @@ -1780,7 +1787,7 @@ def initialize_copy(original_io) # :nodoc:
# the same @fd as the original. That shallow copy is really only
# relevant for primitive values (Fixnum, String, etc) and not
# our own objects. Instantiate a new @fd.
@fd = FileDescriptor.choose_type(fd)
@fd = FileDescriptor.choose_type(fd, dest_io)
dest_io.mode = original_io.mode
dest_io.sync = original_io.sync
dest_io.binmode if original_io.binmode?
Expand All @@ -1791,7 +1798,7 @@ def initialize_copy(original_io) # :nodoc:
private :initialize_copy

def new_pipe(fd, external, internal, options, mode, do_encoding=false)
@fd = FIFOFileDescriptor.new(fd, nil, mode)
@fd = FIFOFileDescriptor.new(fd, nil, self, mode)
@lineno = 0
@pipe = true

Expand Down Expand Up @@ -3009,7 +3016,7 @@ def reopen(other, mode=undefined)
# When reopening we may be going from a Pipe to a File or vice versa. Let the
# system figure out the proper FD class.
@fd.cancel_finalizer # cancel soon-to-be-overwritten instance's finalizer
@fd = FileDescriptor.choose_type(descriptor)
@fd = FileDescriptor.choose_type(descriptor, self)
Rubinius::Unsafe.set_class self, io.class
if io.respond_to?(:path)
@path = io.path
Expand Down Expand Up @@ -3044,7 +3051,7 @@ def reopen(other, mode=undefined)
def reopen_path(path, mode)
status = @fd.reopen_path(path, mode)
@fd.cancel_finalizer
@fd = FileDescriptor.choose_type(descriptor)
@fd = FileDescriptor.choose_type(descriptor, self)
return status
end

Expand Down
21 changes: 21 additions & 0 deletions vm/builtin/io.cpp
Expand Up @@ -1532,6 +1532,27 @@ namespace rubinius {

return Pointer::create(state, ptr);
}

void RIOStream::init(STATE) {
GO(rio_stream).set(ontology::new_class_under(state, "RIOStream", G(rubinius)));
G(rio_stream)->set_object_type(state, RIOStreamType);
}

Object* RIOStream::close(STATE, Object* io, Object* raise_exception) {
// If there is a handle for this IO, and it's been promoted into
// a lowlevel RIO struct using fdopen, then we MUST use fclose
// to close it.

if(capi::Handle* hdl = io->handle(state)) {
if(hdl->is_rio()) {
if(!hdl->rio_close() && CBOOL(raise_exception)) {
Exception::errno_error(state);
}
return cTrue;
}
}
return cFalse;
}

}; // ends namespace rubinius

19 changes: 19 additions & 0 deletions vm/builtin/io.hpp
Expand Up @@ -243,6 +243,25 @@ namespace rubinius {
};
};

class RIOStream : public Object {
public:
const static object_type type = RIOStreamType;

static void init(STATE);

// Rubinius.primitive :rio_close
static Object* close(STATE, Object* io, Object* raise_exception);

class Info : public TypeInfo {
public:
Info(object_type type) : TypeInfo(type) { }
void auto_mark(Object* obj, ObjectMark& mark) { }
void set_field(STATE, Object* target, size_t index, Object* val) { }
Object* get_field(STATE, Object* target, size_t index) { return cNil; }
void populate_slot_locations() { }
};
};

}

#endif
3 changes: 2 additions & 1 deletion vm/globals.hpp
Expand Up @@ -55,7 +55,7 @@ namespace rubinius {
TypedRoot<Class*> nil_class, true_class, false_class, fixnum_class, undef_class;
TypedRoot<Class*> floatpoint, nmc, list, list_node;
TypedRoot<Class*> channel, thread, thread_state, constantscope, constant_table, lookuptable;
TypedRoot<Class*> iseq, executable, native_function, iobuffer, select, fdset;
TypedRoot<Class*> iseq, executable, native_function, iobuffer, select, fdset, rio_stream;
TypedRoot<Class*> included_module;

/* the primary symbol table */
Expand Down Expand Up @@ -178,6 +178,7 @@ namespace rubinius {
iobuffer(&roots),
select(&roots),
fdset(&roots),
rio_stream(&roots),
included_module(&roots),
sym_method_missing(&roots),
sym_respond_to_missing(&roots),
Expand Down

0 comments on commit aafdd94

Please sign in to comment.