[Gauche-devel-jp] dup2(2)のインターフェイス

アーカイブの一覧に戻る

Rui Ueyama rui31****@gmail*****
2007年 8月 12日 (日) 17:08:29 JST


dup2(2)のインターフェイスを作成してみました。どういうインターフェイス
を用意するのが良いか迷ったのですが、次のようにしてみました。

  (port-fd-dup! toport fromport)

  アトミックにtoportのファイルディスクリプタをクローズし、fromportの
  ファイルディスクリプタ番号を複製したものをtoportに設定します。
  toport、fromportはいずれもファイルポートでなければなりません。

前にLingrで議論したときには、toportのfdを閉じてからfromportのfd
をdup2()する間に別スレッドがファイルをオープンすると、toportのfdが
そちらに使われてしまって問題が起こるよね、という話をしたような
気がしますが、このパッチではtoportのfdをクローズせずにdup2()を
呼んでいるのでそういう競合状態はありません。
(manページでは、クローズせずにdup2()を呼ぶとクローズの際のエラー
が失われてしまう旨の記述があるのですが、いまのGaucheはclose()
のエラーを調べていないようなのでそれは問題ないかなと。)

ちなみにdup2()がほしい理由は、Gaucheで書かれたプロセスを
デーモン化するときに、標準入出力を/dev/nullにリダイレクトしたいから
です。たとえば現在の出力を/dev/nullにリダイレクトするコードは、
port-fd-dup!を使うと次のようにかけます。

(call-with-output-file "/dev/null"
  (cut port-fd-dup! (current-output-port) <>))

-- 
植山 類
-------------- next part --------------
Index: doc/corelib.texi
===================================================================
RCS file: /cvsroot/gauche/Gauche/doc/corelib.texi,v
retrieving revision 1.147
diff -u -r1.147 corelib.texi
--- doc/corelib.texi	10 Aug 2007 02:05:50 -0000	1.147
+++ doc/corelib.texi	12 Aug 2007 08:05:45 -0000
@@ -9295,6 +9295,16 @@
 @c COMMON
 @end defun
 
+ @ defun port-fd-dup! toport fromport
+ @ c EN
+ @ c JP
+??????????@code{dup2(2)}??????????????????????
+????????????@var{toport}??????????????????????????????????????@var{fromport}??
+??????????????????????????????????????????@var{toport}??????????????
+ @ var{toport}??@var{fromport}??????????????????????????????????????????????
+ @ c COMMON
+ @ end defun
+
 @node String ports, Coding-aware ports, File ports, Input and output
 @subsection String ports
 @c NODE ????????????
Index: doc/mapping.texi
===================================================================
RCS file: /cvsroot/gauche/Gauche/doc/mapping.texi,v
retrieving revision 1.13
diff -u -r1.13 mapping.texi
--- doc/mapping.texi	17 Jan 2007 08:26:26 -0000	1.13
+++ doc/mapping.texi	12 Aug 2007 08:05:45 -0000
@@ -452,13 +452,9 @@
 @item dup
 @itemx dup2
 @c EN
-No equivalent function.  If you want to use this function to
-set up I/Os for the child process, you can use `iomap' argument
-of @code{sys-exec}.  @xref{Unix process management}.
- @ c JP
-?????????????????????????????????????? I/O ????????????????????????
- @ code{sys-exec} ?? `iomap' ????????????????@ref{Unix process management}
-??????
+Not directly supported, but you can use @code{port-fd-dup!}.
+ @ c JP
+????????????????????????????????@code{port-fd-dup!} ????????????
 @c COMMON
 @item execl
 @itemx execle
Index: src/extlib.stub
===================================================================
RCS file: /cvsroot/gauche/Gauche/src/extlib.stub,v
retrieving revision 1.297
diff -u -r1.297 extlib.stub
--- src/extlib.stub	12 Aug 2007 03:16:55 -0000	1.297
+++ src/extlib.stub	12 Aug 2007 08:05:45 -0000
@@ -953,6 +953,9 @@
   "  int i = Scm_PortFileNo(port);
   SCM_RETURN((i < 0)? SCM_FALSE : Scm_MakeInteger(i));")
 
+(define-cproc port-fd-dup! (dst::<port> src::<port>)
+  (call <void> "Scm_PortFdDup"))
+
 (define-enum SEEK_SET)
 (define-enum SEEK_CUR)
 (define-enum SEEK_END)
Index: src/port.c
===================================================================
RCS file: /cvsroot/gauche/Gauche/src/port.c,v
retrieving revision 1.141
diff -u -r1.141 port.c
--- src/port.c	9 Aug 2007 21:53:48 -0000	1.141
+++ src/port.c	12 Aug 2007 08:05:45 -0000
@@ -238,6 +238,40 @@
     }
 }
 
+/* Duplicates the file descriptor of the source port, and set it to
+   the destination port.  Both source and destination port must be
+   file port. */
+void Scm_PortFdDup(ScmPort *dst, ScmPort *src)
+{
+    int r, srcfd, dstfd;
+
+    if (SCM_PORT_TYPE(dst) != SCM_PORT_FILE)
+        Scm_Error("file port required, but got %S", dst);
+    if (SCM_PORT_TYPE(src) != SCM_PORT_FILE)
+        Scm_Error("file port required, but got %S", src);
+    if (src->direction != dst->direction)
+        Scm_Error("port direction mismatch: got %S and %S",
+                  src, dst);
+        
+    srcfd = (intptr_t)src->src.buf.data;
+    dstfd = (intptr_t)dst->src.buf.data;
+
+    if (dst->direction == SCM_PORT_INPUT) {
+        /* discard the current buffer */
+        ScmVM *vm = Scm_VM();
+        PORT_LOCK(dst, vm);
+        dst->src.buf.current = dst->src.buf.buffer;
+        dst->src.buf.end = dst->src.buf.buffer;
+        PORT_UNLOCK(dst);
+    } else {
+        /* flush the current buffer */
+        Scm_Flush(dst);
+    }
+    SCM_SYSCALL(r, dup2(srcfd, dstfd));
+    if (r < 0) Scm_SysError("dup2 failed");
+    dst->src.buf.data = (void*)(intptr_t)r;
+}
+
 /* Low-level function to find if the file descriptor is ready or not.
    DIR specifies SCM_PORT_INPUT or SCM_PORT_OUTPUT.
    If the system doesn't have select(), this function returns
Index: src/gauche/port.h
===================================================================
RCS file: /cvsroot/gauche/Gauche/src/gauche/port.h,v
retrieving revision 1.24
diff -u -r1.24 port.h
--- src/gauche/port.h	27 Apr 2007 03:37:08 -0000	1.24
+++ src/gauche/port.h	12 Aug 2007 08:05:45 -0000
@@ -272,6 +272,7 @@
 SCM_EXTERN ScmObj Scm_PortSeek(ScmPort *port, ScmObj off, int whence);
 SCM_EXTERN ScmObj Scm_PortSeekUnsafe(ScmPort *port, ScmObj off, int whence);
 SCM_EXTERN int    Scm_PortFileNo(ScmPort *port);
+SCM_EXTERN void   Scm_PortFdDup(ScmPort *dst, ScmPort *src);
 SCM_EXTERN int    Scm_FdReady(int fd, int dir);
 SCM_EXTERN int    Scm_ByteReady(ScmPort *port);
 SCM_EXTERN int    Scm_ByteReadyUnsafe(ScmPort *port);
Index: test/io.scm
===================================================================
RCS file: /cvsroot/gauche/Gauche/test/io.scm,v
retrieving revision 1.28
diff -u -r1.28 io.scm
--- test/io.scm	16 Apr 2007 03:47:13 -0000	1.28
+++ test/io.scm	12 Aug 2007 08:05:45 -0000
@@ -105,6 +105,35 @@
            (call-with-input-file "tmp2.o" read)))
 
 ;;-------------------------------------------------------------------
+(test-section "port-fd-dup!")
+
+(test* "port-fd-dup!" '("foo" "bar")
+       (let* ((p1 (open-output-file "tmp1.o"))
+              (p2 (open-output-file "tmp2.o")))
+         (display "foo\n" p1)
+         (port-fd-dup! p1 p2)
+         (display "bar\n" p1)
+         (close-output-port p1)
+         (close-output-port p2)
+         (list (call-with-input-file "tmp1.o" read-line)
+               (call-with-input-file "tmp2.o" read-line))))
+
+(test* "port-fd-dup!" '("foo" "bar")
+       (let* ((p1 (open-input-file "tmp1.o"))
+              (p2 (open-input-file "tmp2.o"))
+              (s1 (read-line p1)))
+         (port-fd-dup! p1 p2)
+         (list s1 (read-line p1))))
+
+(test* "port-fd-dup!" *test-error*
+       (port-fd-dup! (open-output-file "tmp1.o")
+                     (open-input-file "tmp2.o")))
+
+(test* "port-fd-dup!" *test-error*
+       (port-fd-dup! (open-input-string "")
+                     (open-input-file "tmp2.o")))
+
+;;-------------------------------------------------------------------
 (test-section "input ports")
 
 (sys-unlink "tmp1.o")


Gauche-devel-jp メーリングリストの案内
アーカイブの一覧に戻る