/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <utils.h>
#include "fdpool.h"
#define INVALID_FD (-1)
#define FDPOOL_SIZE 4
static struct pooled_fd fdpool_head = {
.fd = INVALID_FD,
.prev = &fdpool_head,
.next = &fdpool_head
};
static int fdpool_count = 0;
static void fdpool_insert_head(struct pooled_fd *node)
{
struct pooled_fd *prev = &fdpool_head;
struct pooled_fd *next = prev->next;
assert(node);
prev->next = node;
node->prev = prev;
node->next = next;
next->prev = node;
fdpool_count++;
}
static void fdpool_remove(struct pooled_fd *node)
{
struct pooled_fd *prev = node->prev;
struct pooled_fd *next = node->next;
assert(prev);
assert(next);
prev->next = next;
next->prev = prev;
fdpool_count--;
}
static struct pooled_fd *fdpool_remove_tail(void)
{
struct pooled_fd *tail = fdpool_head.prev;
assert(tail != &fdpool_head);
fdpool_remove(tail);
return tail;
}
static void fdpool_clear(struct pooled_fd *pfd)
{
assert(pfd);
pfd->fd = INVALID_FD;
pfd->prev = pfd->next = NULL;
}
static void fdpool_unpool(struct pooled_fd *pfd)
{
close(pfd->fd);
fdpool_clear(pfd);
}
static void fdpool_evict(void)
{
struct pooled_fd *tail;
tail = fdpool_remove_tail();
fdpool_unpool(tail);
}
static void fdpool_pool(struct pooled_fd *pfd, int fd)
{
if (fdpool_count >= FDPOOL_SIZE)
fdpool_evict();
fdpool_insert_head(pfd);
pfd->fd = fd;
}
static void fdpool_touch(struct pooled_fd *pfd)
{
fdpool_remove(pfd);
fdpool_insert_head(pfd);
}
void fdpool_init(struct pooled_fd *pfd)
{
fdpool_clear(pfd);
}
int fdpool_open(struct pooled_fd *pfd, const char *pathname, int flags)
{
int open_errno;
int fd;
if (pfd->fd != INVALID_FD) {
fdpool_touch(pfd);
return pfd->fd;
}
fd = open(pathname, flags);
open_errno = errno;
if (fd >= 0) {
fdpool_pool(pfd, fd);
}
errno = open_errno;
return fd;
}
void fdpool_close(struct pooled_fd *pfd)
{
assert(pfd);
fdpool_unpool(pfd);
}