guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: patch for mmap and friends


From: Matt Wette
Subject: Re: patch for mmap and friends
Date: Sat, 14 Jan 2023 14:08:17 -0800
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.4.2

On 1/14/23 8:31 AM, Matt Wette wrote:
On 1/14/23 7:18 AM, Maxime Devos wrote:
\
Port objects should be accepted too, as previously asked on <https://lists.gnu.org/archive/html/guile-user/2022-06/msg00060.html>.
As implied by later comments, using a raw fd causes problems with 'move->fdes'.  For the remaining response, I'll assume that the function accepts ports as well.


To avoid this problem, you can add

  scm_remember_upto_here_1 (fd);

after the SCM_SYSCALL.
\


IIRC there is a C trick involving fields, arrays and types to check this at compile-time instead.  Maybe:

struct whatever {
   /* if availability of zero-length arrays can be assumed */
   int foo[sizeof(size_t) - sizeof(void*)];
   /* alternatively, a weaker but portable check */
   int foo[sizeof(size_t) - sizeof(void*) + 1];
};

Greetings,
Maxime.

Thanks for the feedback.   I'm sorry I missed you comments on the previous round.
I did respond to the ones I did catch.    I'll work this and resubmit.

Matt



Here is another shot.
1) added port support
2) used dynwind to protect port/fd. 
3) removed the assert: I don't know why I put it in there.

Notes:
1) four other guile headers needed: atomic-internal, foreign, finalizers and ioext
2) had to copy/modify dynwind_acquire_port and release_port from ports.c
3) one maybe-kludge is if an fd is passed I use (car (fd->ports fd)) for acquire_port

Matt

static void
mmap_finalizer (void *ptr, void *data)
{
  SCM bvec;
  void *c_addr;
  size_t c_len;
  int rv;

  bvec = SCM_PACK_POINTER (ptr);
  if (!SCM_BYTEVECTOR_P (bvec))
    scm_misc_error ("mmap", "expecting bytevector", SCM_EOL);

  c_addr = SCM_BYTEVECTOR_CONTENTS (bvec);
  c_len = SCM_BYTEVECTOR_LENGTH (bvec);
  SCM_SYSCALL (rv = munmap(c_addr, c_len));
  if (rv != 0)
    scm_misc_error ("mmap", "failed to munmap memory", SCM_EOL);
}

/* Code for scm_dynwind_acquire_port and release_port sourced from ports.c. */

static void
release_port (SCM port)
{
  scm_t_port *pt = SCM_PORT (port);
  uint32_t cur = 1, next = 0;
  while (!scm_atomic_compare_and_swap_uint32 (&pt->refcount, &cur, next))
    {
      if (cur == 0)
        return;
      next = cur - 1;
    }
 if (cur > 1)
    return;

  if (SCM_PORT_TYPE (port)->close)
    SCM_PORT_TYPE (port)->close (port);

  /* Skip encoding code from ports.c! */
}

static void
scm_dynwind_acquire_port (SCM port)
{
  scm_t_port *pt = SCM_PORT (port);
  uint32_t cur = 1, next = 2;
  while (!scm_atomic_compare_and_swap_uint32 (&pt->refcount, &cur, next))
    {
      if (cur == 0)
        scm_wrong_type_arg_msg (NULL, 0, port, "open port");
      next = cur + 1;
    }
  scm_dynwind_unwind_handler_with_scm (release_port, port,
                                       SCM_F_WIND_EXPLICITLY);
}

SCM_DEFINE (scm_mmap_search, "mmap/search", 2, 4, 0,
            (SCM addr, SCM len, SCM prot, SCM flags, SCM file, SCM offset),
        "Create a memory mapping, returning a bytevector..  @var{addr},\n"
        "if non-zero, is the staring address; or, if zero, is assigned by\n"
        "the system.  @var{prot}, if provided, assigns protection.\n"
        "@var{file}, a port or fd, if provided associates the memory\n"
            "region with a file starting at @var{offset}, if provided.\n"
        "The region returned by mmap WILL be searched by the garbage\n"
        "collector for pointers.  See also mmap.  Note that the\n"
            "finalizer for the returned bytevector will call munmap.\n"
        "Defaults for optional arguments are\n"
        "@table @asis\n"
        "@item prot\n(logior PROT_READ PROT_WRITE)\n"
        "@item flags\n(logior MAP_ANONYMOUS MAP_PRIVATE)\n"
        "@item fd\n-1\n"
        "@item offset\n0\n"
        "@end table")
#define FUNC_NAME s_scm_mmap_search
{
  void *c_mem, *c_addr;
  size_t c_len;
  int c_prot, c_flags, c_fd;
  scm_t_off c_offset;
  SCM pointer, bvec;

  if (SCM_POINTER_P (addr))
    c_addr = SCM_POINTER_VALUE (addr);
  else if (scm_is_integer (addr))
    c_addr = (void*) scm_to_uintptr_t (addr);
  else
    scm_misc_error ("mmap", "bad addr", addr);

  c_len = scm_to_size_t (len);

  if (SCM_UNBNDP (prot))
    c_prot = PROT_READ | PROT_WRITE;
  else
    c_prot = scm_to_int (prot);

  if (SCM_UNBNDP (flags))
    c_flags = MAP_ANONYMOUS | MAP_PRIVATE;
  else
    c_flags = scm_to_int (flags);

  scm_dynwind_begin (0);
 
  if (SCM_UNBNDP (file))
    c_fd = -1;
  else {
    /* Use the fd under clobber protection from GC or another thread. */
    if (SCM_PORTP (file))
      c_fd = scm_to_int (scm_fileno (file));
    else {
      c_fd = scm_to_int (file);
      file = SCM_CAR (scm_fdes_to_ports (file));
    }
    scm_dynwind_acquire_port (file);
  }

  if (SCM_UNBNDP (offset))
    c_offset = 0;
  else
    c_offset = scm_to_off_t (offset);

  if ((c_addr == NULL) && (c_flags & MAP_FIXED))
    scm_misc_error ("mmap", "cannot have NULL addr w/ MAP_FIXED", SCM_EOL);

  SCM_SYSCALL (c_mem = mmap(c_addr, c_len, c_prot, c_flags, c_fd, c_offset));
  if (c_mem == MAP_FAILED)
    scm_syserror ("mmap");              /* errno set */

  /* The fd is free to go now. */
  scm_dynwind_end ();

  pointer = scm_cell (scm_tc7_pointer, (scm_t_bits) c_mem);
  bvec = scm_c_take_typed_bytevector((signed char *) c_mem + c_offset, c_len,
                     SCM_ARRAY_ELEMENT_TYPE_VU8, pointer);
  scm_i_set_finalizer (SCM2PTR (bvec), mmap_finalizer, (void*) c_len);
  return bvec;
}
#undef FUNC_NAME



  






reply via email to

[Prev in Thread] Current Thread [Next in Thread]