Annotation of libelwix/src/ring.c, revision 1.10

1.2       misho       1: /*************************************************************************
                      2: * (C) 2025 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
                      3: *  by Michael Pounov <misho@elwix.org>
                      4: *
                      5: * $Author: misho $
1.10    ! misho       6: * $Id: ring.c,v 1.9.4.1 2026/07/27 20:12:35 misho Exp $
1.2       misho       7: *
                      8: **************************************************************************
                      9: The ELWIX and AITNET software is distributed under the following
                     10: terms:
                     11: 
                     12: All of the documentation and software included in the ELWIX and AITNET
                     13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
                     14: 
1.5       misho      15: Copyright 2004 - 2026
1.2       misho      16:        by Michael Pounov <misho@elwix.org>.  All rights reserved.
                     17: 
                     18: Redistribution and use in source and binary forms, with or without
                     19: modification, are permitted provided that the following conditions
                     20: are met:
                     21: 1. Redistributions of source code must retain the above copyright
                     22:    notice, this list of conditions and the following disclaimer.
                     23: 2. Redistributions in binary form must reproduce the above copyright
                     24:    notice, this list of conditions and the following disclaimer in the
                     25:    documentation and/or other materials provided with the distribution.
                     26: 3. All advertising materials mentioning features or use of this software
                     27:    must display the following acknowledgement:
                     28: This product includes software developed by Michael Pounov <misho@elwix.org>
                     29: ELWIX - Embedded LightWeight unIX and its contributors.
                     30: 4. Neither the name of AITNET nor the names of its contributors
                     31:    may be used to endorse or promote products derived from this software
                     32:    without specific prior written permission.
                     33: 
                     34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
                     35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     37: ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     44: SUCH DAMAGE.
                     45: */
                     46: #include "global.h"
                     47: 
                     48: 
                     49: /*
                     50:  * rbuf_init() - Init ring buffer
                     51:  *
                     52:  * @rbuf = Ring buffer
                     53:  * @num = Number of elements in buffer
                     54:  * return: -1 error or 0 ok
                     55:  */
                     56: int
                     57: rbuf_init(ringbuf_t *rbuf, int num)
                     58: {
                     59:        if (!rbuf)
                     60:                return -1;
                     61: 
1.9       misho      62:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_release);
                     63:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
                     64:        atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
1.2       misho      65: 
                     66:        rbuf->rb_buffer = e_calloc(num, sizeof(struct iovec));
                     67:        if (!rbuf->rb_buffer)
                     68:                return -1;
                     69:        else
                     70:                rbuf->rb_bufnum = num;
                     71:        memset(rbuf->rb_buffer, 0, num * sizeof(struct iovec));
                     72: 
                     73:        return 0;
                     74: }
                     75: 
                     76: /*
                     77:  * rbuf_free() - Free ring buffer
                     78:  *
                     79:  * @rbuf = Ring buffer
                     80:  * return: none
                     81:  */
                     82: void
                     83: rbuf_free(ringbuf_t *rbuf)
                     84: {
                     85:        if (!rbuf)
                     86:                return;
                     87: 
                     88:        if (rbuf->rb_buffer) {
                     89:                e_free(rbuf->rb_buffer);
                     90:                rbuf->rb_buffer = NULL;
                     91:                rbuf->rb_bufnum = 0;
                     92:        }
                     93: 
1.9       misho      94:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_release);
                     95:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
                     96:        atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
1.2       misho      97: }
                     98: 
                     99: /*
                    100:  * rbuf_purge() - Purge all buffer
                    101:  *
                    102:  * @rbuf = Ring buffer
                    103:  * return: none
                    104:  */
                    105: void
                    106: rbuf_purge(ringbuf_t *rbuf)
                    107: {
                    108:        if (!rbuf)
                    109:                return;
                    110: 
                    111:        if (rbuf->rb_buffer)
                    112:                memset(rbuf->rb_buffer, 0, rbuf->rb_bufnum * sizeof(struct iovec));
                    113: 
1.9       misho     114:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, 0, memory_order_release);
                    115:        atomic_store_explicit((atomic_int*) &rbuf->rb_tail, 0, memory_order_release);
                    116:        atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
1.2       misho     117: }
                    118: 
                    119: /*
                    120:  * rbuf_isempty() - Check buffer is empty
                    121:  *
                    122:  * @rbuf = Ring buffer
                    123:  * return: -1 error, 0 it isn't empty
                    124:  */
                    125: int
                    126: rbuf_isempty(ringbuf_t *rbuf)
                    127: {
1.8       misho     128:        if (!rbuf || !rbuf->rb_bufnum)
1.2       misho     129:                return -1;
                    130: 
1.8       misho     131:        if (atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire))
                    132:                return 0;
                    133: 
1.3       misho     134:        return (atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire) ==
1.8       misho     135:                        atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire));
1.2       misho     136: }
                    137: 
                    138: /*
                    139:  * rbuf_isfull() - Check buffer is full
                    140:  *
                    141:  * @rbuf = Ring buffer
                    142:  * return: -1 error or 0 it isn't full
                    143:  */
                    144: int
                    145: rbuf_isfull(ringbuf_t *rbuf)
                    146: {
1.8       misho     147:        int h, t;
                    148: 
                    149:        if (!rbuf || !rbuf->rb_bufnum)
1.2       misho     150:                return -1;
                    151: 
1.8       misho     152:        if (!atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire))
                    153:                return 0;
                    154: 
                    155:        t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
                    156:        h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
                    157:        return (h == t);
1.2       misho     158: }
                    159: 
                    160: /*
                    161:  * rbuf_enqueue() - Enqueue data to buffer
                    162:  *
                    163:  * @rbuf = Ring buffer
                    164:  * @data = Data
                    165:  * @len = Length
1.8       misho     166:  * @lost = Permit to lost data
1.2       misho     167:  * return: -1 error, 1 buffer is full or 0 ok
                    168:  */
                    169: int
1.8       misho     170: rbuf_enqueue(ringbuf_t *rbuf, void *data, size_t len, int lost)
1.2       misho     171: {
1.8       misho     172:        int h, t, f, n, t2, drop = 0;
1.2       misho     173:        struct iovec *iov;
                    174: 
1.8       misho     175:        if (!rbuf || !rbuf->rb_buffer || !rbuf->rb_bufnum)
1.2       misho     176:                return -1;
                    177: 
1.8       misho     178:        f = atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire);
1.3       misho     179:        t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
1.8       misho     180:        h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
                    181: 
                    182:        if (f && h == t) {
                    183:                if (!lost)
                    184:                        return 1;
                    185:                else
                    186:                        drop = 1;
                    187:        }
                    188: 
1.2       misho     189:        n = (h + 1) % rbuf->rb_bufnum;
                    190: 
                    191:        iov = rbuf->rb_buffer + h;
                    192:        iov->iov_len = len;
                    193:        iov->iov_base = data;
                    194: 
1.3       misho     195:        atomic_store_explicit((atomic_int*) &rbuf->rb_head, n, memory_order_release);
1.8       misho     196:        if (drop) {
                    197:                t2 = (t + 1) % rbuf->rb_bufnum;
                    198:                while (42) {
                    199:                        drop = t;
                    200:                        if (atomic_compare_exchange_weak_explicit((atomic_int*) &rbuf->rb_tail,
                    201:                                                &drop, t2, memory_order_release, memory_order_relaxed))
                    202:                                break;
                    203:                        t = drop;
                    204:                        t2 = (t + 1) % rbuf->rb_bufnum;
                    205:                }
                    206:        } else
                    207:                t2 = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
                    208:        atomic_store_explicit((atomic_int*) &rbuf->rb_full, (n == t2), memory_order_release);
1.2       misho     209:        return 0;
                    210: }
                    211: 
                    212: /*
                    213:  * rbuf_dequeue() - Dequeue data from buffer
                    214:  *
                    215:  * @rbuf = Ring buffer
                    216:  * @out = Data, if =NULL, just dequeue data
1.10    ! misho     217:  * @peek = Peek only data, keep the same tail position
1.2       misho     218:  * return: -1 error, 1 buffer is empty or 0 ok
                    219:  */
                    220: int
1.10    ! misho     221: rbuf_dequeue(ringbuf_t *rbuf, struct iovec **out, int peek)
1.2       misho     222: {
1.8       misho     223:        int h, t, n, f;
1.2       misho     224: 
1.8       misho     225:        if (!rbuf || !rbuf->rb_buffer || !rbuf->rb_bufnum)
1.2       misho     226:                return -1;
                    227: 
1.8       misho     228:        while (42) {
                    229:                h = atomic_load_explicit((atomic_int*) &rbuf->rb_head, memory_order_acquire);
                    230:                f = atomic_load_explicit((atomic_int*) &rbuf->rb_full, memory_order_acquire);
                    231:                t = atomic_load_explicit((atomic_int*) &rbuf->rb_tail, memory_order_acquire);
                    232: 
                    233:                if (!f && h == t)
                    234:                        return 1;
                    235: 
                    236:                n = (t + 1) % rbuf->rb_bufnum;
1.2       misho     237: 
1.10    ! misho     238:                if (out)
        !           239:                        *out = rbuf->rb_buffer + t;
        !           240: 
        !           241:                if (peek)
        !           242:                        break;
        !           243: 
1.8       misho     244:                f = t;
                    245:                if (atomic_compare_exchange_weak_explicit((atomic_int*) &rbuf->rb_tail,
                    246:                                        &f, n, memory_order_release, memory_order_relaxed)) {
                    247:                        atomic_store_explicit((atomic_int*) &rbuf->rb_full, 0, memory_order_release);
                    248:                        break;
                    249:                }
                    250:        }
1.2       misho     251: 
                    252:        return 0;
                    253: }
1.5       misho     254: 
                    255: 
                    256: /*
                    257:  * lrb_init() - Init linear ring buffer
                    258:  *
                    259:  * @lrb = Linear ring buffer
                    260:  * @size = Size of ring buffer
                    261:  * return: -1 error or 0 ok
                    262:  */
                    263: int
                    264: lrb_init(lrbuf_t *lrb, u_int size)
                    265: {
                    266:        if (!lrb)
                    267:                return -1;
                    268: 
1.9       misho     269:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
                    270:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
                    271:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
1.5       misho     272: 
                    273:        lrb->lrb_data = e_malloc(size);
                    274:        if (!lrb->lrb_data)
                    275:                return -1;
                    276:        else
                    277:                lrb->lrb_size = size;
                    278:        memset(lrb->lrb_data, 0, lrb->lrb_size);
                    279: 
                    280:        return 0;
                    281: }
                    282: 
                    283: /*
                    284:  * lrb_free() - Free linear ring buffer
                    285:  *
                    286:  * @lrb = Linear ring buffer
                    287:  * return: none
                    288:  */
                    289: void
                    290: lrb_free(lrbuf_t *lrb)
                    291: {
                    292:        if (!lrb)
                    293:                return;
                    294: 
                    295:        if (lrb->lrb_data) {
                    296:                e_free(lrb->lrb_data);
                    297:                lrb->lrb_data = NULL;
                    298:                lrb->lrb_size = 0;
                    299:        }
                    300: 
1.9       misho     301:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
                    302:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
                    303:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
1.5       misho     304: }
                    305: 
                    306: /*
                    307:  * lrb_purge() - Purge all buffer
                    308:  *
                    309:  * @lrb = Linear ring buffer
                    310:  * return: none
                    311:  */
                    312: void
                    313: lrb_purge(lrbuf_t *lrb)
                    314: {
                    315:        if (!lrb)
                    316:                return;
                    317: 
                    318:        if (lrb->lrb_data)
                    319:                memset(lrb->lrb_data, 0, lrb->lrb_size);
                    320: 
1.9       misho     321:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, 0, memory_order_release);
                    322:        atomic_store_explicit((atomic_int*) &lrb->lrb_tail, 0, memory_order_release);
                    323:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
1.5       misho     324: }
                    325: 
                    326: /*
                    327:  * lrb_isempty() - Check buffer is empty
                    328:  *
                    329:  * @lrb = Linear ring buffer
                    330:  * return: -1 error, 0 it isn't empty
                    331:  */
                    332: int
                    333: lrb_isempty(lrbuf_t *lrb)
                    334: {
1.8       misho     335:        if (!lrb || !lrb->lrb_size)
1.5       misho     336:                return -1;
                    337: 
1.8       misho     338:        if (atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
                    339:                return 0;
                    340: 
                    341:        return (atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire) ==
                    342:                        atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire));
1.5       misho     343: }
                    344: 
                    345: /*
                    346:  * lrb_isfull() - Check buffer is full
                    347:  *
                    348:  * @lrb = Linear ring buffer
                    349:  * return: -1 error or 0 it isn't full
                    350:  */
                    351: int
                    352: lrb_isfull(lrbuf_t *lrb)
                    353: {
                    354:        int h, t;
                    355: 
1.8       misho     356:        if (!lrb || !lrb->lrb_size)
1.5       misho     357:                return -1;
                    358: 
                    359:        if (!atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire))
                    360:                return 0;
                    361: 
                    362:        t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    363:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
                    364:        return (h == t);
                    365: }
                    366: 
                    367: /*
1.6       misho     368:  * lrb_getw() - Get address for write
                    369:  *
                    370:  * @lrb = Linear ring buffer
                    371:  * @len = Return available buffer length for write
                    372:  * return: NULL error or !=NULL pointer for write
                    373:  * remark: After use of lrb_getw() and write to pointer.
                    374:  *             You should update ring buffer with lrb_enqueue(,NULL,wrote_len,)
                    375:  */
                    376: void *
                    377: lrb_getw(lrbuf_t *lrb, size_t *len)
                    378: {
                    379:        int h;
                    380: 
                    381:        if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
                    382:                return NULL;
                    383: 
1.9       misho     384:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
1.6       misho     385:        if (len)
                    386:                *len = lrb->lrb_size - h;
                    387: 
                    388:        return (lrb->lrb_data + h);
                    389: }
                    390: 
                    391: /*
1.5       misho     392:  * lrb_enqueue() - Enqueue data to buffer
                    393:  *
                    394:  * @lrb = Linear ring buffer
                    395:  * @data = Data
                    396:  * @len = Length
                    397:  * @lost = Permit to lost data
                    398:  * return: -1 error, 1 buffer is full or 0 ok
                    399:  */
                    400: int
                    401: lrb_enqueue(lrbuf_t *lrb, void *data, size_t len, int lost)
                    402: {
1.6       misho     403:        int h, t = 0, n, t2 = 0, unused, drop = 0;
1.5       misho     404: 
1.8       misho     405:        if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
1.5       misho     406:                return -1;
1.8       misho     407:        if (lrb->lrb_size <= len)
1.5       misho     408:                return 1;
                    409: 
                    410:        lrb_unused(lrb, unused);
                    411:        if (!lost) {
                    412:                if (len > unused)
                    413:                        return 1;
                    414:        } else {
                    415:                drop = len - unused;
                    416:                if(drop < 0)
                    417:                        drop ^= drop;
                    418:        }
                    419: 
                    420:        if (drop > 0) {
                    421:                t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    422:                t2 = (t + drop) % lrb->lrb_size;
                    423:        }
1.9       misho     424:        h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
1.5       misho     425:        n = lrb->lrb_size - h;
                    426:        if (len < n) {
1.6       misho     427:                if (data)
                    428:                        memcpy(lrb->lrb_data + h, data, len);
1.5       misho     429:                n = h + len;
                    430:        } else {
1.6       misho     431:                if (data) {
                    432:                        memcpy(lrb->lrb_data + h, data, n);
                    433:                        memcpy(lrb->lrb_data, data + n, len - n);
                    434:                }
1.5       misho     435:                n = len - n;
                    436:        }
                    437: 
                    438:        h = n;
                    439:        atomic_store_explicit((atomic_int*) &lrb->lrb_head, h, memory_order_release);
                    440:        if (drop > 0)
                    441:                while (42) {
                    442:                        n = t;
                    443:                        if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
                    444:                                                &n, t2, memory_order_release, memory_order_relaxed))
                    445:                                break;
                    446:                        t = n;
                    447:                        t2 = (t + drop) % lrb->lrb_size;
                    448:                }
                    449:        else
                    450:                t2 = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    451:        atomic_store_explicit((atomic_int*) &lrb->lrb_full, (h == t2), memory_order_release);
                    452:        return 0;
                    453: }
                    454: 
                    455: /*
1.6       misho     456:  * lrb_getr() - Get address for read
                    457:  *
                    458:  * @lrb = Linear ring buffer
                    459:  * @len = Return available data length for read
                    460:  * return: NULL error or !=NULL pointer for read
                    461:  * remark: After use of lrb_getr() and read from pointer.
                    462:  *             You could update ring buffer with lrb_dequeue(,NULL,read_len)
                    463:  */
                    464: void *
                    465: lrb_getr(lrbuf_t *lrb, size_t *len)
                    466: {
                    467:        int t;
                    468: 
                    469:        if (!lrb || !lrb->lrb_data || !lrb->lrb_size)
                    470:                return NULL;
                    471: 
                    472:        t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    473:        if (len)
                    474:                lrb_queued(lrb, *len);
                    475: 
                    476:        return (lrb->lrb_data + t);
                    477: }
                    478: 
                    479: /*
1.5       misho     480:  * lrb_dequeue() - Dequeue data from buffer
                    481:  *
                    482:  * @lrb = Linear ring buffer
                    483:  * @data = Data, if =NULL, just dequeue data
                    484:  * @len = Length of data
1.10    ! misho     485:  * @peek = Peek only data, keep the same tail position
1.5       misho     486:  * return: -1 error, 0 buffer is empty or >0 stored data bytes
                    487:  */
                    488: int
1.10    ! misho     489: lrb_dequeue(lrbuf_t *lrb, void *data, size_t len, int peek)
1.5       misho     490: {
                    491:        int h, t, t2, n, l, f;
                    492: 
1.8       misho     493:        if (!lrb || !lrb->lrb_size)
1.5       misho     494:                return -1;
1.8       misho     495:        if (!len || lrb_isempty(lrb))
1.5       misho     496:                return 0;
                    497:        if (lrb->lrb_size <= len)
                    498:                len = lrb->lrb_size - 1;
                    499: 
                    500:        while (42) {
                    501:                t = atomic_load_explicit((atomic_int*) &lrb->lrb_tail, memory_order_acquire);
                    502:                h = atomic_load_explicit((atomic_int*) &lrb->lrb_head, memory_order_acquire);
                    503:                f = atomic_load_explicit((atomic_int*) &lrb->lrb_full, memory_order_acquire);
                    504: 
                    505:                l = h - t;
                    506:                if (l < 0)
                    507:                        l += lrb->lrb_size;
                    508:                if (!l) {
                    509:                        if (!f)
                    510:                                return 0;
                    511:                        l = lrb->lrb_size;
                    512:                }
                    513:                if (l > len)
                    514:                        l = len;
                    515: 
                    516:                n = lrb->lrb_size - t;
                    517:                if (l < n) {
                    518:                        if (data)
                    519:                                memcpy(data, lrb->lrb_data + t, l);
                    520:                        t2 = t + l;
                    521:                } else {
                    522:                        if (data) {
                    523:                                memcpy(data, lrb->lrb_data + t, n);
1.8       misho     524:                                memcpy(((u_char*) data) + n, lrb->lrb_data, l - n);
1.5       misho     525:                        }
                    526:                        t2 = l - n;
                    527:                }
                    528: 
1.10    ! misho     529:                if (peek)
        !           530:                        return l;
        !           531: 
1.5       misho     532:                n = t;
                    533:                if (atomic_compare_exchange_weak_explicit((atomic_int*) &lrb->lrb_tail,
                    534:                                        &n, t2, memory_order_release, memory_order_relaxed)) {
                    535:                        atomic_store_explicit((atomic_int*) &lrb->lrb_full, 0, memory_order_release);
                    536:                        return l;
                    537:                }
                    538:        }
                    539: 
                    540:        return 0;
                    541: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>