Pipes/FIFOs: Implement the unlink method. If the pipe/FIFO is unlinked, it will marked the pipe/FIFO as unliked. If/when all open references to the driver are closed, all of the driver resources will be freed.

This commit is contained in:
Gregory Nutt 2015-01-31 12:05:01 -06:00
parent 05e82a88a7
commit 39a5238c43
4 changed files with 116 additions and 43 deletions

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* drivers/pipes/fifo.c * drivers/pipes/fifo.c
* *
* Copyright (C) 2008-2009, 2014 Gregory Nutt. All rights reserved. * Copyright (C) 2008-2009, 2014-2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -73,10 +73,11 @@ static const struct file_operations fifo_fops =
pipecommon_read, /* read */ pipecommon_read, /* read */
pipecommon_write, /* write */ pipecommon_write, /* write */
0, /* seek */ 0, /* seek */
pipecommon_ioctl /* ioctl */ pipecommon_ioctl, /* ioctl */
#ifndef CONFIG_DISABLE_POLL #ifndef CONFIG_DISABLE_POLL
, pipecommon_poll /* poll */ pipecommon_poll, /* poll */
#endif #endif
pipecommon_unlink /* unlink */
}; };
/**************************************************************************** /****************************************************************************
@ -92,15 +93,15 @@ static const struct file_operations fifo_fops =
* *
* Description: * Description:
* mkfifo() makes a FIFO device driver file with name 'pathname.' Unlike * mkfifo() makes a FIFO device driver file with name 'pathname.' Unlike
* Linux, a NuttX FIFO is not a special file type but simply a device driver * Linux, a NuttX FIFO is not a special file type but simply a device
* instance. 'mode' specifies the FIFO's permissions. * driver instance. 'mode' specifies the FIFO's permissions.
* *
* Once the FIFO has been created by mkfifo(), any thread can open it for * Once the FIFO has been created by mkfifo(), any thread can open it for
* reading or writing, in the same way as an ordinary file. However, it must * reading or writing, in the same way as an ordinary file. However, it
* have been opened from both reading and writing before input or output * must have been opened from both reading and writing before input or
* can be performed. This FIFO implementation will block all attempts to * output can be performed. This FIFO implementation will block all
* open a FIFO read-only until at least one thread has opened the FIFO for * attempts to open a FIFO read-only until at least one thread has opened
* writing. * the FIFO for writing.
* *
* If all threads that write to the FIFO have closed, subsequent calls to * If all threads that write to the FIFO have closed, subsequent calls to
* read() on the FIFO will return 0 (end-of-file). * read() on the FIFO will return 0 (end-of-file).

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* drivers/pipes/pipe.c * drivers/pipes/pipe.c
* *
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. * Copyright (C) 2008-2009, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -82,10 +82,11 @@ static const struct file_operations pipe_fops =
pipecommon_read, /* read */ pipecommon_read, /* read */
pipecommon_write, /* write */ pipecommon_write, /* write */
0, /* seek */ 0, /* seek */
pipecommon_ioctl /* ioctl */ pipecommon_ioctl, /* ioctl */
#ifndef CONFIG_DISABLE_POLL #ifndef CONFIG_DISABLE_POLL
, pipecommon_poll /* poll */ pipecommon_poll, /* poll */
#endif #endif
pipecommon_unlink /* unlink */
}; };
static sem_t g_pipesem = SEM_INITIALIZER(1); static sem_t g_pipesem = SEM_INITIALIZER(1);
@ -124,7 +125,9 @@ static inline int pipe_allocate(void)
static inline void pipe_free(int pipeno) static inline void pipe_free(int pipeno)
{ {
int ret = sem_wait(&g_pipesem); int ret;
ret = sem_wait(&g_pipesem);
if (ret == 0) if (ret == 0)
{ {
g_pipeset &= ~(1 << pipeno); g_pipeset &= ~(1 << pipeno);
@ -138,17 +141,11 @@ static inline void pipe_free(int pipeno)
static int pipe_close(FAR struct file *filep) static int pipe_close(FAR struct file *filep)
{ {
struct inode *inode = filep->f_inode; FAR struct inode *inode = filep->f_inode;
struct pipe_dev_s *dev = inode->i_private; FAR struct pipe_dev_s *dev = inode->i_private;
int ret; int ret;
/* Some sanity checking */ DEBUGASSERT(dev);
#if CONFIG_DEBUG
if (!dev)
{
return -EBADF;
}
#endif
/* Perform common close operations */ /* Perform common close operations */
@ -171,8 +168,8 @@ static int pipe_close(FAR struct file *filep)
* Name: pipe * Name: pipe
* *
* Description: * Description:
* pipe() creates a pair of file descriptors, pointing to a pipe inode, and * pipe() creates a pair of file descriptors, pointing to a pipe inode,
* places them in the array pointed to by 'fd'. fd[0] is for reading, * and places them in the array pointed to by 'fd'. fd[0] is for reading,
* fd[1] is for writing. * fd[1] is for writing.
* *
* Inputs: * Inputs:
@ -187,7 +184,7 @@ static int pipe_close(FAR struct file *filep)
int pipe(int fd[2]) int pipe(int fd[2])
{ {
struct pipe_dev_s *dev = NULL; FAR struct pipe_dev_s *dev = NULL;
char devname[16]; char devname[16];
int pipeno; int pipeno;
int err; int err;
@ -272,15 +269,19 @@ int pipe(int fd[2])
errout_with_wrfd: errout_with_wrfd:
close(fd[1]); close(fd[1]);
errout_with_driver: errout_with_driver:
unregister_driver(devname); unregister_driver(devname);
errout_with_dev: errout_with_dev:
if (dev) if (dev)
{ {
pipecommon_freedev(dev); pipecommon_freedev(dev);
} }
errout_with_pipe: errout_with_pipe:
pipe_free(pipeno); pipe_free(pipeno);
errout: errout:
set_errno(err); set_errno(err);
return ERROR; return ERROR;

View File

@ -172,10 +172,10 @@ FAR struct pipe_dev_s *pipecommon_allocdev(void)
void pipecommon_freedev(FAR struct pipe_dev_s *dev) void pipecommon_freedev(FAR struct pipe_dev_s *dev)
{ {
sem_destroy(&dev->d_bfsem); sem_destroy(&dev->d_bfsem);
sem_destroy(&dev->d_rdsem); sem_destroy(&dev->d_rdsem);
sem_destroy(&dev->d_wrsem); sem_destroy(&dev->d_wrsem);
kmm_free(dev); kmm_free(dev);
} }
/**************************************************************************** /****************************************************************************
@ -203,9 +203,9 @@ int pipecommon_open(FAR struct file *filep)
return -get_errno(); return -get_errno();
} }
/* If this the first reference on the device, then allocate the buffer. In the /* If this the first reference on the device, then allocate the buffer.
* case of d_policy == 1, the buffer already be present when the pipe is * In the case of policy 1, the buffer already be present when the pipe
* first opened. * is first opened.
*/ */
if (dev->d_refs == 0 && dev->d_buffer == NULL) if (dev->d_refs == 0 && dev->d_buffer == NULL)
@ -303,7 +303,7 @@ int pipecommon_close(FAR struct file *filep)
pipecommon_semtake(&dev->d_bfsem); pipecommon_semtake(&dev->d_bfsem);
/* Decrement the number of references on the pipe. Check if there are /* Decrement the number of references on the pipe. Check if there are
* still oustanding references to the pipe. * still outstanding references to the pipe.
*/ */
/* Check if the decremented reference count would go to zero */ /* Check if the decremented reference count would go to zero */
@ -331,12 +331,12 @@ int pipecommon_close(FAR struct file *filep)
} }
/* What is the buffer management policy? Do we free the buffe when the /* What is the buffer management policy? Do we free the buffe when the
* last client closes the pipe (d_policy == 0), or when the buffer becomes * last client closes the pipe policy 0, or when the buffer becomes empty.
* empty (d_policy). In the latter case, the buffer data will remain * In the latter case, the buffer data will remain valid and can be
* valid and can be obtained when the pipe is re-opened. * obtained when the pipe is re-opened.
*/ */
else if (dev->d_policy == 0 || dev->d_wrndx == dev->d_rdndx) else if (PIPE_IS_POLICY_0(dev->d_flags) || dev->d_wrndx == dev->d_rdndx)
{ {
/* Policy 0 or the buffer is empty ... deallocate the buffer now. */ /* Policy 0 or the buffer is empty ... deallocate the buffer now. */
@ -349,6 +349,16 @@ int pipecommon_close(FAR struct file *filep)
dev->d_rdndx = 0; dev->d_rdndx = 0;
dev->d_refs = 0; dev->d_refs = 0;
dev->d_nwriters = 0; dev->d_nwriters = 0;
/* If, in addition, we have been unlinked, then also need to free the
* device structure as well to prevent a memory leak.
*/
if (PIPE_IS_UNLINKED(dev->d_flags))
{
pipecommon_freedev(dev);
return OK;
}
} }
sem_post(&dev->d_bfsem); sem_post(&dev->d_bfsem);
@ -678,10 +688,57 @@ int pipecommon_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
if (cmd == PIPEIOC_POLICY) if (cmd == PIPEIOC_POLICY)
{ {
dev->d_policy = (arg != 0); if (arg != 0)
{
PIPE_POLICY_1(dev->d_flags);
}
else
{
PIPE_POLICY_0(dev->d_flags);
}
return OK; return OK;
} }
return -ENOTTY; return -ENOTTY;
} }
/****************************************************************************
* Name: pipecommon_unlink
****************************************************************************/
int pipecommon_unlink(FAR void *priv)
{
/* The value passed to pipcommon_unlink is the private data pointer from
* the inode that is being unlinked. If there are no open references to
* the driver, then we must free up all resources used by the driver now.
*/
FAR struct pipe_dev_s *dev = (FAR struct pipe_dev_s *)priv;
DEBUGASSERT(dev);
/* Mark the pipe unlinked */
PIPE_UNLINK(dev->d_flags);
/* Are the any open references to the driver? */
if (dev->d_refs == 0)
{
/* No.. free the buffer (if there is one) */
if (dev->d_buffer)
{
kmm_free(dev->d_buffer);
}
/* And free the device structure. */
pipecommon_freedev(dev);
}
return OK;
}
#endif /* CONFIG_DEV_PIPE_SIZE > 0 */ #endif /* CONFIG_DEV_PIPE_SIZE > 0 */

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* drivers/pipe/pipe_common.h * drivers/pipe/pipe_common.h
* *
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved. * Copyright (C) 2008-2009, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -67,6 +67,20 @@
#define CONFIG_DEV_PIPE_MAXUSER 255 #define CONFIG_DEV_PIPE_MAXUSER 255
/* d_flags values */
#define PIPE_FLAG_POLICY (1 << 0) /* Bit 0: Policy=Free buffer when empty */
#define PIPE_FLAG_UNLINKED (1 << 1) /* Bit 1: The driver has been unlinked */
#define PIPE_POLICY_0(f) do { (f) &= ~PIPE_FLAG_POLICY; } while (0)
#define PIPE_POLICY_1(f) do { (f) |= PIPE_FLAG_POLICY; } while (0)
#define PIPE_IS_POLICY_0(f) (((f) & PIPE_FLAG_POLICY) == 0)
#define PIPE_IS_POLICY_1(f) (((f) & PIPE_FLAG_POLICY) != 0)
#define PIPE_UNLINK(f) do { (f) |= PIPE_FLAG_UNLINKED; } while (0)
#define PIPE_IS_UNLINKED(f) (((f) & PIPE_FLAG_UNLINKED) != 0)
/**************************************************************************** /****************************************************************************
* Public Types * Public Types
****************************************************************************/ ****************************************************************************/
@ -96,7 +110,7 @@ struct pipe_dev_s
uint8_t d_refs; /* References counts on pipe (limited to 255) */ uint8_t d_refs; /* References counts on pipe (limited to 255) */
uint8_t d_nwriters; /* Number of reference counts for write access */ uint8_t d_nwriters; /* Number of reference counts for write access */
uint8_t d_pipeno; /* Pipe minor number */ uint8_t d_pipeno; /* Pipe minor number */
bool d_policy; /* Buffer policy: 0=free on close; 1=free on empty */ uint8_t d_flags; /* See PIPE_FLAG_* definitions */
uint8_t *d_buffer; /* Buffer allocated when device opened */ uint8_t *d_buffer; /* Buffer allocated when device opened */
/* The following is a list if poll structures of threads waiting for /* The following is a list if poll structures of threads waiting for
@ -127,11 +141,11 @@ int pipecommon_close(FAR struct file *filep);
ssize_t pipecommon_read(FAR struct file *, FAR char *, size_t); ssize_t pipecommon_read(FAR struct file *, FAR char *, size_t);
ssize_t pipecommon_write(FAR struct file *, FAR const char *, size_t); ssize_t pipecommon_write(FAR struct file *, FAR const char *, size_t);
int pipecommon_ioctl(FAR struct file *filep, int cmd, unsigned long arg); int pipecommon_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
#ifndef CONFIG_DISABLE_POLL #ifndef CONFIG_DISABLE_POLL
int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds, int pipecommon_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup); bool setup);
#endif #endif
int pipecommon_unlink(FAR void *priv);
#undef EXTERN #undef EXTERN
#ifdef __cplusplus #ifdef __cplusplus