sleepy_penguin 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +0 -1
- data/Rakefile +0 -37
- data/ext/sleepy_penguin/epoll.c +3 -15
- data/ext/sleepy_penguin/extconf.rb +9 -2
- data/ext/sleepy_penguin/init.c +59 -1
- data/ext/sleepy_penguin/inotify.c +16 -36
- data/ext/sleepy_penguin/kqueue.c +3 -15
- data/ext/sleepy_penguin/missing_clock_gettime.h +36 -0
- data/ext/sleepy_penguin/sleepy_penguin.h +1 -0
- data/ext/sleepy_penguin/util.c +16 -8
- data/ext/sleepy_penguin/value2timespec.h +2 -2
- data/test/helper.rb +17 -0
- data/test/test_constants.rb +2 -2
- data/test/test_epoll.rb +10 -9
- data/test/test_epoll_gc.rb +2 -2
- data/test/test_epoll_io.rb +2 -2
- data/test/test_epoll_optimizations.rb +2 -2
- data/test/test_eventfd.rb +3 -7
- data/test/test_inotify.rb +3 -7
- data/test/test_kqueue.rb +2 -2
- data/test/test_kqueue_io.rb +2 -2
- data/test/test_timerfd.rb +3 -7
- metadata +65 -60
- data/ext/sleepy_penguin/signalfd.c +0 -342
- data/test/test_signalfd.rb +0 -94
- data/test/test_signalfd_siginfo.rb +0 -32
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
data/Rakefile
CHANGED
@@ -31,40 +31,3 @@ task :publish_news do
|
|
31
31
|
rf.login
|
32
32
|
rf.post_news('rainbows', subject, body)
|
33
33
|
end
|
34
|
-
|
35
|
-
desc "post to RAA"
|
36
|
-
task :raa_update do
|
37
|
-
require 'net/http'
|
38
|
-
require 'net/netrc'
|
39
|
-
rc = Net::Netrc.locate('sleepy_penguin-raa') or abort "~/.netrc not found"
|
40
|
-
password = rc.password
|
41
|
-
|
42
|
-
s = Gem::Specification.load('sleepy_penguin.gemspec')
|
43
|
-
desc = [ s.description.strip ]
|
44
|
-
desc << ""
|
45
|
-
desc << "* #{s.email}"
|
46
|
-
desc << "* #{git_url}"
|
47
|
-
desc << "* #{cgit_url}"
|
48
|
-
desc = desc.join("\n")
|
49
|
-
uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
|
50
|
-
form = {
|
51
|
-
:name => s.name,
|
52
|
-
:short_description => s.summary,
|
53
|
-
:version => s.version.to_s,
|
54
|
-
:status => 'experimental',
|
55
|
-
:owner => s.authors.first,
|
56
|
-
:email => s.email,
|
57
|
-
:category_major => 'Library',
|
58
|
-
:category_minor => 'System',
|
59
|
-
:url => s.homepage,
|
60
|
-
:download => 'http://rubyforge.org/frs/?group_id=8977',
|
61
|
-
:license => "LGPL",
|
62
|
-
:description_style => 'Plain',
|
63
|
-
:description => desc,
|
64
|
-
:pass => password,
|
65
|
-
:submit => 'Update',
|
66
|
-
}
|
67
|
-
res = Net::HTTP.post_form(uri, form)
|
68
|
-
p res
|
69
|
-
puts res.body
|
70
|
-
end
|
data/ext/sleepy_penguin/epoll.c
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#include "sleepy_penguin.h"
|
2
2
|
#ifdef HAVE_SYS_EPOLL_H
|
3
3
|
#include <sys/epoll.h>
|
4
|
-
#include <unistd.h>
|
5
4
|
#include <time.h>
|
5
|
+
#include "missing_clock_gettime.h"
|
6
6
|
#include "missing_epoll.h"
|
7
7
|
#include "missing_rb_thread_fd_close.h"
|
8
8
|
#include "missing_rb_update_max_fd.h"
|
@@ -51,10 +51,8 @@ static int ep_fd_check(struct ep_per_thread *ept)
|
|
51
51
|
|
52
52
|
static struct ep_per_thread *ept_get(VALUE self, int maxevents)
|
53
53
|
{
|
54
|
-
|
54
|
+
struct ep_per_thread *ept;
|
55
55
|
size_t size;
|
56
|
-
int err;
|
57
|
-
void *ptr;
|
58
56
|
|
59
57
|
/* error check here to prevent OOM from posix_memalign */
|
60
58
|
if (maxevents <= 0) {
|
@@ -62,21 +60,11 @@ static struct ep_per_thread *ept_get(VALUE self, int maxevents)
|
|
62
60
|
rb_sys_fail("epoll_wait maxevents <= 0");
|
63
61
|
}
|
64
62
|
|
65
|
-
if (ept && ept->capa >= maxevents)
|
66
|
-
goto out;
|
67
|
-
|
68
63
|
size = sizeof(struct ep_per_thread) +
|
69
64
|
sizeof(struct epoll_event) * maxevents;
|
70
65
|
|
71
|
-
|
72
|
-
err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, size);
|
73
|
-
if (err) {
|
74
|
-
errno = err;
|
75
|
-
rb_memerror();
|
76
|
-
}
|
77
|
-
ept = ptr;
|
66
|
+
ept = rb_sp_gettlsbuf(&size);
|
78
67
|
ept->capa = maxevents;
|
79
|
-
out:
|
80
68
|
ept->maxevents = maxevents;
|
81
69
|
ept->io = self;
|
82
70
|
ept->fd = rb_sp_fileno(ept->io);
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
have_header('sys/epoll.h')
|
3
3
|
dir_config('kqueue')
|
4
|
-
|
5
|
-
|
4
|
+
if have_header('sys/event.h')
|
5
|
+
have_library('kqueue')
|
6
|
+
end
|
6
7
|
have_header('sys/mount.h')
|
7
8
|
have_header('sys/eventfd.h')
|
8
9
|
|
@@ -13,6 +14,11 @@ have_header('sys/eventfd.h')
|
|
13
14
|
have_header('sys/timerfd.h')
|
14
15
|
have_header('sys/inotify.h')
|
15
16
|
have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
|
17
|
+
unless have_macro('CLOCK_MONOTONIC', 'time.h')
|
18
|
+
have_func('CLOCK_MONOTONIC', 'time.h')
|
19
|
+
end
|
20
|
+
have_type('clockid_t', 'time.h')
|
21
|
+
have_func('clock_gettime', 'time.h')
|
16
22
|
have_func('epoll_create1', %w(sys/epoll.h))
|
17
23
|
have_func('rb_thread_call_without_gvl')
|
18
24
|
have_func('rb_thread_blocking_region')
|
@@ -20,4 +26,5 @@ have_func('rb_thread_io_blocking_region')
|
|
20
26
|
have_func('rb_thread_fd_close')
|
21
27
|
have_func('rb_update_max_fd')
|
22
28
|
have_func('rb_fd_fix_cloexec')
|
29
|
+
have_func('rb_io_get_io')
|
23
30
|
create_makefile('sleepy_penguin_ext')
|
data/ext/sleepy_penguin/init.c
CHANGED
@@ -1,10 +1,20 @@
|
|
1
|
-
#define _GNU_SOURCE
|
2
1
|
#include <ruby.h>
|
2
|
+
#ifndef _GNU_SOURCE
|
3
|
+
# define _GNU_SOURCE /* TODO: confirm this is needed */
|
4
|
+
#endif
|
5
|
+
|
3
6
|
#include <unistd.h>
|
7
|
+
#include <pthread.h>
|
4
8
|
#include <sys/types.h>
|
5
9
|
#include "git_version.h"
|
10
|
+
#include "sleepy_penguin.h"
|
6
11
|
#define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
|
7
12
|
size_t rb_sp_l1_cache_line_size;
|
13
|
+
static pthread_key_t rb_sp_key;
|
14
|
+
struct rb_sp_tlsbuf {
|
15
|
+
size_t capa;
|
16
|
+
unsigned char ptr[FLEX_ARRAY];
|
17
|
+
};
|
8
18
|
|
9
19
|
#ifdef HAVE_SYS_EVENT_H
|
10
20
|
void sleepy_penguin_init_kqueue(void);
|
@@ -53,9 +63,57 @@ static size_t l1_cache_line_size_detect(void)
|
|
53
63
|
return L1_CACHE_LINE_MAX;
|
54
64
|
}
|
55
65
|
|
66
|
+
static void sp_once(void)
|
67
|
+
{
|
68
|
+
int err = pthread_key_create(&rb_sp_key, free);
|
69
|
+
|
70
|
+
if (err) {
|
71
|
+
errno = err;
|
72
|
+
rb_sys_fail( "pthread_key_create");
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
void *rb_sp_gettlsbuf(size_t *size)
|
77
|
+
{
|
78
|
+
struct rb_sp_tlsbuf *buf = pthread_getspecific(rb_sp_key);
|
79
|
+
void *ptr;
|
80
|
+
int err;
|
81
|
+
size_t bytes;
|
82
|
+
|
83
|
+
if (buf && buf->capa >= *size) {
|
84
|
+
*size = buf->capa;
|
85
|
+
goto out;
|
86
|
+
}
|
87
|
+
|
88
|
+
free(buf);
|
89
|
+
bytes = *size + sizeof(struct rb_sp_tlsbuf);
|
90
|
+
err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, bytes);
|
91
|
+
if (err) {
|
92
|
+
errno = err;
|
93
|
+
rb_memerror(); /* fatal */
|
94
|
+
}
|
95
|
+
|
96
|
+
buf = ptr;
|
97
|
+
buf->capa = *size;
|
98
|
+
err = pthread_setspecific(rb_sp_key, buf);
|
99
|
+
if (err != 0) {
|
100
|
+
errno = err;
|
101
|
+
rb_sys_fail("BUG: pthread_setspecific");
|
102
|
+
}
|
103
|
+
out:
|
104
|
+
return buf->ptr;
|
105
|
+
}
|
106
|
+
|
56
107
|
void Init_sleepy_penguin_ext(void)
|
57
108
|
{
|
58
109
|
VALUE mSleepyPenguin;
|
110
|
+
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
111
|
+
int err = pthread_once(&once, sp_once);
|
112
|
+
|
113
|
+
if (err) {
|
114
|
+
errno = err;
|
115
|
+
rb_sys_fail("pthread_once");
|
116
|
+
}
|
59
117
|
|
60
118
|
rb_sp_l1_cache_line_size = l1_cache_line_size_detect();
|
61
119
|
|
@@ -4,11 +4,6 @@
|
|
4
4
|
#include <sys/ioctl.h>
|
5
5
|
#include "missing_inotify.h"
|
6
6
|
|
7
|
-
struct inbuf {
|
8
|
-
size_t capa;
|
9
|
-
void *ptr;
|
10
|
-
};
|
11
|
-
|
12
7
|
static ID id_inotify_tmp, id_mask;
|
13
8
|
static VALUE cEvent, checks;
|
14
9
|
|
@@ -137,43 +132,31 @@ static VALUE event_new(struct inotify_event *e)
|
|
137
132
|
|
138
133
|
struct inread_args {
|
139
134
|
int fd;
|
140
|
-
|
135
|
+
size_t size;
|
136
|
+
void *buf;
|
141
137
|
};
|
142
138
|
|
143
139
|
static VALUE inread(void *ptr)
|
144
140
|
{
|
145
141
|
struct inread_args *args = ptr;
|
146
142
|
|
147
|
-
return (VALUE)read(args->fd, args->
|
148
|
-
}
|
149
|
-
|
150
|
-
static void inbuf_grow(struct inbuf *inbuf, size_t size)
|
151
|
-
{
|
152
|
-
int err;
|
153
|
-
|
154
|
-
if (inbuf->capa >= size)
|
155
|
-
return;
|
156
|
-
free(inbuf->ptr);
|
157
|
-
err = posix_memalign(&inbuf->ptr, rb_sp_l1_cache_line_size, size);
|
158
|
-
if (err) {
|
159
|
-
errno = err;
|
160
|
-
rb_memerror();
|
161
|
-
}
|
162
|
-
inbuf->capa = size;
|
143
|
+
return (VALUE)read(args->fd, args->buf, args->size);
|
163
144
|
}
|
164
145
|
|
165
146
|
static void resize_internal_buffer(struct inread_args *args)
|
166
147
|
{
|
167
148
|
int newlen;
|
168
149
|
|
169
|
-
if (args->
|
150
|
+
if (args->size > 0x10000)
|
170
151
|
rb_raise(rb_eRuntimeError, "path too long");
|
171
152
|
|
172
153
|
if (ioctl(args->fd, FIONREAD, &newlen) != 0)
|
173
154
|
rb_sys_fail("ioctl(inotify,FIONREAD)");
|
174
155
|
|
175
|
-
if (newlen > 0)
|
176
|
-
|
156
|
+
if (newlen > 0) {
|
157
|
+
args->size = (size_t)newlen;
|
158
|
+
args->buf = rb_sp_gettlsbuf(&args->size);
|
159
|
+
}
|
177
160
|
|
178
161
|
if (newlen == 0) /* race: some other thread grabbed the data */
|
179
162
|
return;
|
@@ -192,8 +175,6 @@ static void resize_internal_buffer(struct inread_args *args)
|
|
192
175
|
*/
|
193
176
|
static VALUE take(int argc, VALUE *argv, VALUE self)
|
194
177
|
{
|
195
|
-
static __thread struct inbuf inbuf;
|
196
|
-
|
197
178
|
struct inread_args args;
|
198
179
|
VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
|
199
180
|
struct inotify_event *e, *end;
|
@@ -206,9 +187,9 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
206
187
|
|
207
188
|
rb_scan_args(argc, argv, "01", &nonblock);
|
208
189
|
|
209
|
-
inbuf_grow(&inbuf, 128);
|
210
190
|
args.fd = rb_sp_fileno(self);
|
211
|
-
args.
|
191
|
+
args.size = 128;
|
192
|
+
args.buf = rb_sp_gettlsbuf(&args.size);
|
212
193
|
|
213
194
|
if (RTEST(nonblock))
|
214
195
|
rb_sp_set_nonblock(args.fd);
|
@@ -228,9 +209,8 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
228
209
|
rb_sys_fail("read(inotify)");
|
229
210
|
} else {
|
230
211
|
/* buffer in userspace to minimize read() calls */
|
231
|
-
end = (struct inotify_event *)
|
232
|
-
|
233
|
-
for (e = args.inbuf->ptr; e < end; ) {
|
212
|
+
end = (struct inotify_event *)((char *)args.buf + r);
|
213
|
+
for (e = args.buf; e < end; ) {
|
234
214
|
VALUE event = event_new(e);
|
235
215
|
if (NIL_P(rv))
|
236
216
|
rv = event;
|
@@ -255,15 +235,15 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
|
|
255
235
|
static VALUE events(VALUE self)
|
256
236
|
{
|
257
237
|
long len = RARRAY_LEN(checks);
|
258
|
-
|
238
|
+
long i;
|
259
239
|
VALUE sym;
|
260
240
|
VALUE rv = rb_ary_new();
|
261
241
|
uint32_t mask;
|
262
242
|
uint32_t event_mask = NUM2UINT(rb_funcall(self, id_mask, 0));
|
263
243
|
|
264
|
-
for (
|
265
|
-
sym =
|
266
|
-
mask = NUM2UINT(
|
244
|
+
for (i = 0; i < len; ) {
|
245
|
+
sym = rb_ary_entry(checks, i++);
|
246
|
+
mask = NUM2UINT(rb_ary_entry(checks, i++));
|
267
247
|
if ((event_mask & mask) == mask)
|
268
248
|
rb_ary_push(rv, sym);
|
269
249
|
}
|
data/ext/sleepy_penguin/kqueue.c
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
#include <sys/time.h>
|
6
6
|
#include <unistd.h>
|
7
7
|
#include <time.h>
|
8
|
+
#include "missing_clock_gettime.h"
|
8
9
|
#include "missing_rb_thread_fd_close.h"
|
9
10
|
#include "missing_rb_update_max_fd.h"
|
10
11
|
#include "value2timespec.h"
|
@@ -73,10 +74,8 @@ static int kq_fd_check(struct kq_per_thread *kpt)
|
|
73
74
|
|
74
75
|
static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
|
75
76
|
{
|
76
|
-
|
77
|
+
struct kq_per_thread *kpt;
|
77
78
|
size_t size;
|
78
|
-
void *ptr;
|
79
|
-
int err;
|
80
79
|
int max = nchanges > nevents ? nchanges : nevents;
|
81
80
|
|
82
81
|
/* error check here to prevent OOM from posix_memalign */
|
@@ -85,20 +84,9 @@ static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
|
|
85
84
|
rb_sys_fail("kevent got negative events < 0");
|
86
85
|
}
|
87
86
|
|
88
|
-
if (kpt && kpt->capa >= max)
|
89
|
-
goto out;
|
90
|
-
|
91
87
|
size = sizeof(struct kq_per_thread) + sizeof(struct kevent) * max;
|
92
|
-
|
93
|
-
free(kpt); /* free(NULL) is POSIX */
|
94
|
-
err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, size);
|
95
|
-
if (err) {
|
96
|
-
errno = err;
|
97
|
-
rb_memerror();
|
98
|
-
}
|
99
|
-
kpt = ptr;
|
88
|
+
kpt = rb_sp_gettlsbuf(&size);
|
100
89
|
kpt->capa = max;
|
101
|
-
out:
|
102
90
|
kpt->nchanges = nchanges;
|
103
91
|
kpt->nevents = nevents;
|
104
92
|
kpt->io = self;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/*
|
2
|
+
* this header includes functions to support broken systems
|
3
|
+
* without clock_gettime() or CLOCK_MONOTONIC
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef HAVE_TYPE_CLOCKID_T
|
7
|
+
typedef int clockid_t;
|
8
|
+
#endif
|
9
|
+
|
10
|
+
#ifndef HAVE_CLOCK_GETTIME
|
11
|
+
# ifndef CLOCK_REALTIME
|
12
|
+
# define CLOCK_REALTIME 0 /* whatever */
|
13
|
+
# endif
|
14
|
+
static int fake_clock_gettime(clockid_t clk_id, struct timespec *res)
|
15
|
+
{
|
16
|
+
struct timeval tv;
|
17
|
+
int r = gettimeofday(&tv, NULL);
|
18
|
+
|
19
|
+
assert(0 == r && "gettimeofday() broke!?");
|
20
|
+
res->tv_sec = tv.tv_sec;
|
21
|
+
res->tv_nsec = tv.tv_usec * 1000;
|
22
|
+
|
23
|
+
return r;
|
24
|
+
}
|
25
|
+
# define clock_gettime fake_clock_gettime
|
26
|
+
#endif /* broken systems w/o clock_gettime() */
|
27
|
+
|
28
|
+
/*
|
29
|
+
* UGH
|
30
|
+
* CLOCK_MONOTONIC is not guaranteed to be a macro, either
|
31
|
+
*/
|
32
|
+
#ifndef CLOCK_MONOTONIC
|
33
|
+
# if (!defined(_POSIX_MONOTONIC_CLOCK) || !defined(HAVE_CLOCK_MONOTONIC))
|
34
|
+
# define CLOCK_MONOTONIC CLOCK_REALTIME
|
35
|
+
# endif
|
36
|
+
#endif
|
@@ -77,6 +77,7 @@ static inline VALUE fake_blocking_region(VALUE (*fn)(void *), void *data)
|
|
77
77
|
|
78
78
|
typedef int rb_sp_waitfn(int fd);
|
79
79
|
int rb_sp_wait(rb_sp_waitfn waiter, VALUE obj, int *fd);
|
80
|
+
void *rb_sp_gettlsbuf(size_t *size);
|
80
81
|
|
81
82
|
/* Flexible array elements are standard in C99 */
|
82
83
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
data/ext/sleepy_penguin/util.c
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
#include "sleepy_penguin.h"
|
2
2
|
|
3
|
+
#ifndef HAVE_RB_IO_GET_IO
|
4
|
+
static VALUE my_io_get_io(VALUE io)
|
5
|
+
{
|
6
|
+
return rb_convert_type(io, T_FILE, "IO", "to_io");
|
7
|
+
}
|
8
|
+
# define rb_io_get_io(io) my_io_get_io((io))
|
9
|
+
#endif /* HAVE_RB_IO_GET_IO */
|
10
|
+
|
3
11
|
static VALUE klass_for(VALUE klass)
|
4
12
|
{
|
5
13
|
return (TYPE(klass) == T_CLASS) ? klass : CLASS_OF(klass);
|
@@ -14,13 +22,13 @@ int rb_sp_get_flags(VALUE klass, VALUE flags, int default_flags)
|
|
14
22
|
case T_SYMBOL:
|
15
23
|
return NUM2INT(rb_const_get(klass_for(klass), SYM2ID(flags)));
|
16
24
|
case T_ARRAY: {
|
17
|
-
|
25
|
+
long i;
|
18
26
|
long len = RARRAY_LEN(flags);
|
19
27
|
int rv = 0;
|
20
28
|
|
21
29
|
klass = klass_for(klass);
|
22
|
-
|
23
|
-
VALUE tmp =
|
30
|
+
for (i = 0; i < len; i++) {
|
31
|
+
VALUE tmp = rb_ary_entry(flags, i);
|
24
32
|
|
25
33
|
Check_Type(tmp, T_SYMBOL);
|
26
34
|
tmp = rb_const_get(klass, SYM2ID(tmp));
|
@@ -42,13 +50,13 @@ unsigned rb_sp_get_uflags(VALUE klass, VALUE flags)
|
|
42
50
|
case T_SYMBOL:
|
43
51
|
return NUM2UINT(rb_const_get(klass_for(klass), SYM2ID(flags)));
|
44
52
|
case T_ARRAY: {
|
45
|
-
|
53
|
+
long i;
|
46
54
|
long len = RARRAY_LEN(flags);
|
47
55
|
unsigned rv = 0;
|
48
56
|
|
49
57
|
klass = klass_for(klass);
|
50
|
-
|
51
|
-
VALUE tmp =
|
58
|
+
for (i = 0; i < len; i++) {
|
59
|
+
VALUE tmp = rb_ary_entry(flags, i);
|
52
60
|
|
53
61
|
Check_Type(tmp, T_SYMBOL);
|
54
62
|
tmp = rb_const_get(klass, SYM2ID(tmp));
|
@@ -100,7 +108,7 @@ int rb_sp_io_closed(VALUE io)
|
|
100
108
|
case T_FILE:
|
101
109
|
break;
|
102
110
|
default:
|
103
|
-
io =
|
111
|
+
io = rb_io_get_io(io);
|
104
112
|
}
|
105
113
|
|
106
114
|
return my_rb_io_closed(io);
|
@@ -110,7 +118,7 @@ int rb_sp_fileno(VALUE io)
|
|
110
118
|
{
|
111
119
|
rb_io_t *fptr;
|
112
120
|
|
113
|
-
io =
|
121
|
+
io = rb_io_get_io(io);
|
114
122
|
GetOpenFile(io, fptr);
|
115
123
|
return FPTR_TO_FD(fptr);
|
116
124
|
}
|