/* $Id: lock.c,v 1.3 2002/05/02 16:16:30 poettering Exp $ * * This file is part of libshbuf. * * asd is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your * option) any later version. * * asd is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with libshbuf; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include "shbuf.h" #include "internal.h" #include "thread.h" #include "shbuferr.h" #ifndef MIN #define MIN(a,b) ((a) > (b) ? (b) : (a)) #endif int shbuf_status_lock(shbuf* sb) { int r; struct sembuf buf; assert(sb); buf.sem_num = 0; buf.sem_op = -1; buf.sem_flg = 0; if ((r = semop(sb->semid, &buf, 1)) != 0) { shbuf_set_errno(SHBUF_LOCKFAILED); return -1; } return 0; } int shbuf_status_unlock(shbuf* sb) { struct sembuf buf; int r; assert(sb); buf.sem_num = 0; buf.sem_op = 1; buf.sem_flg = 0; if ((r = semop(sb->semid, &buf, 1)) != 0) { shbuf_set_errno(SHBUF_UNLOCKFAILED); return -1; } return 0; } int shbuf_reset(shbuf* sb) { assert(sb); if (shbuf_status_lock(sb) < 0) return -1; sb->control->status.read_idx = sb->control->status.length = 0; sb->control->status.ignore_read_inc = sb->control->status.ignore_write_inc = 1; sb->control->status.backlog = 0; // Question: what to do when unlock fails? shbuf_status_unlock(sb); return 0; } int shbuf_zero(shbuf* sb) { assert(sb); if (shbuf_status_lock(sb) < 0) return -1; sb->control->status.read_idx = sb->control->status.length = 0; sb->control->status.ignore_read_inc = sb->control->status.ignore_write_inc = 1; sb->control->status.backlog = 0; memset(sb->buffer, 0, sb->control->size); shbuf_status_unlock(sb); return 0; } unsigned char* shbuf_get_read_pointer(shbuf *sb, unsigned long *l) { unsigned char *p; assert(sb && l); if (shbuf_status_lock(sb) < 0) return (unsigned char*) -1; if (sb->control->status.length) { *l = MIN(sb->control->status.length, sb->control->size - sb->control->status.read_idx); p = sb->buffer + sb->control->status.read_idx; } else { *l = 0; p = 0; } sb->control->status.ignore_read_inc = 0; shbuf_status_unlock(sb); return p; } int shbuf_inc_read_pointer(shbuf *sb, unsigned long r) { assert(sb && r); if (shbuf_status_lock(sb) != 0) return -1; if (!sb->control->status.ignore_read_inc) { if (r > sb->control->status.length) r = sb->control->status.length; sb->control->status.length -= r; sb->control->status.read_idx += r; sb->control->status.read_idx %= sb->control->size; sb->control->status.read_count+= r; sb->control->status.backlog += r; if (sb->control->status.backlog > sb->control->status.backlog_target) sb->control->status.backlog = sb->control->status.backlog_target; } shbuf_status_unlock(sb); return 0; } unsigned char* shbuf_get_write_pointer(shbuf *sb, unsigned long *l) { unsigned char *p; assert(sb && l); if (shbuf_status_lock(sb) < 0) return (unsigned char*) -1; if (sb->control->status.length + sb->control->status.backlog < sb->control->size) { unsigned long write_idx = sb->control->status.read_idx + sb->control->status.length; write_idx %= sb->control->size; *l = MIN(sb->control->size - sb->control->status.length - sb->control->status.backlog, sb->control->size - write_idx); p = sb->buffer + write_idx; } else { *l = 0; p = 0; } sb->control->status.ignore_write_inc = 0; shbuf_status_unlock(sb); return p; } int shbuf_inc_write_pointer(shbuf *sb, unsigned long r) { assert(sb && r); if (shbuf_status_lock(sb) < 0) return -1; if (!sb->control->status.ignore_write_inc) { unsigned long foo = sb->control->size - sb->control->status.length; if (r > foo) r = foo; sb->control->status.length += r; sb->control->status.write_count+= r; if (sb->control->status.length + sb->control->status.backlog > sb->control->size) sb->control->status.backlog = sb->control->size - sb->control->status.length; } shbuf_status_unlock(sb); return 0; } int shbuf_connected(shbuf *sb) { int r; assert(sb); if (sb->is_dead) return 0; if (shbuf_status_lock(sb) < 0) return -1; r = sb->control->client_attached && sb->control->provider_attached; shbuf_status_unlock(sb); return r; } int shbuf_notify_enable(shbuf* sb, int b) { assert(sb); if (shbuf_status_lock(sb) < 0) return -1; if (sb->is_provider) sb->control->provider_notify = b ? 1 : 0; else sb->control->client_notify = b ? 1 : 0; shbuf_status_unlock(sb); if (b) return thread_start(sb); else thread_stop(sb); return 0; } int shbuf_is_empty(shbuf *sb) { unsigned long r; assert(sb); if (shbuf_status_lock(sb) < 0) return -1; r = sb->control->status.length; shbuf_status_unlock(sb); return r == 0 ? 1 : 0; } int shbuf_is_full(shbuf *sb) { unsigned long r; assert(sb); if (shbuf_status_lock(sb) < 0) return -1; r = sb->control->size - sb->control->status.length - sb->control->status.backlog; shbuf_status_unlock(sb); return r <= 0 ? 1 : 0; } unsigned long shbuf_rewind(shbuf *sb, unsigned long v) { unsigned long r; if (shbuf_status_lock(sb) < 0) return (unsigned long) -1; if (v == 0) r = sb->control->status.backlog; else { r = MIN(v, sb->control->status.backlog); sb->control->status.backlog -= r; sb->control->status.length += r; if (sb->control->status.read_idx < r) { sb->control->status.read_idx = sb->control->size; r -= sb->control->status.read_idx; } sb->control->status.read_idx -= r; } shbuf_status_unlock(sb); return r; } signed long shbuf_write(shbuf *sb, unsigned char*c, signed long l) { unsigned long _l; unsigned char *_c; assert(sb && c && l > 0); do { if ((_c = shbuf_get_write_pointer(sb, &_l)) == (unsigned char*) -1) return -1; if (shbuf_wait(sb) != 0) return -1; } while (!_c); _l = MIN(l, _l); memcpy(_c, c, _l); return shbuf_inc_write_pointer(sb, _l); } signed long shbuf_read(shbuf *sb, unsigned char*c, signed long l) { unsigned long _l; unsigned char *_c; assert(sb && c && l > 0); do { if ((_c = shbuf_get_read_pointer(sb, &_l)) == (unsigned char*) -1) return -1; if (shbuf_wait(sb) != 0) return -1; } while (!_c); _l = MIN(l, _l); memcpy(c, _c, _l); return shbuf_inc_read_pointer(sb, _l); } int shbuf_set_backlog_target(shbuf *sb, unsigned long bl) { if (shbuf_status_lock(sb) < 0) return -1; if (bl < sb->control->size) sb->control->status.backlog_target = bl; else sb->control->status.backlog_target = sb->control->size -1; shbuf_status_unlock(sb); return 0; } unsigned long shbuf_get_backlog_target(shbuf *sb) { unsigned long r; if (shbuf_status_lock(sb) < 0) return (unsigned long) -1; r = sb->control->status.backlog_target; shbuf_status_unlock(sb); return r; }