| /* |
| * 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); |
| } |