File:  [Coherent Logic Development] / freem / src / locktab.c
Revision 1.5: download - view: text, annotated - select for diffs
Mon Mar 24 02:57:25 2025 UTC (8 days, 21 hours ago) by snw
Branches: MAIN
CVS tags: v0-62-3, HEAD
Shared memory compatibility fixes for OS/2

/*
 *   $Id: locktab.c,v 1.5 2025/03/24 02:57:25 snw Exp $
 *    lock table implementation
 *
 *  
 *   Author: Serena Willis <snw@coherent-logic.com>
 *    Copyright (C) 1998 MUG Deutschland
 *    Copyright (C) 2021, 2025 Coherent Logic Development LLC
 *
 *
 *   This file is part of FreeM.
 *
 *   FreeM is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   FreeM 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 Affero Public License for more details.
 *
 *   You should have received a copy of the GNU Affero Public License
 *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
 *
 *   $Log: locktab.c,v $
 *   Revision 1.5  2025/03/24 02:57:25  snw
 *   Shared memory compatibility fixes for OS/2
 *
 *   Revision 1.4  2025/03/09 19:14:25  snw
 *   First phase of REUSE compliance and header reformat
 *
 *
 * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
 * SPDX-License-Identifier: AGPL-3.0-or-later
 **/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

#include "mpsdef.h"
#include "locktab.h"
#include "shmmgr.h"
#include "mref.h"
#include "transact.h"

#if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                (Linux-specific) */
};
#endif

int semid_locktab;

int locktab_list_count(char *key);

void locktab_init(void)
{
    union semun arg;
    key_t lt_sk;

    lt_sk = ftok (config_file, 3);
    
    if (first_process) {

        semid_locktab = semget (lt_sk, 1, 0666 | IPC_CREAT);
        if (semid_locktab == -1) {
            fprintf (stderr, "locktab_init:  failed to create lock table semaphore\r\n");
            exit (1);
        }

        arg.val = 1;
        if (semctl (semid_locktab, 0, SETVAL, arg) == -1) {
            fprintf (stderr, "locktab_init:  failed to initialize lock table semaphore\r\n");
            exit (1);
        }
        
    }
    else {

        semid_locktab = semget (lt_sk, 1, 0);
        if (semid_locktab == -1) {
            fprintf (stderr, "locktab_init:  could not attach to lock table semaphore\r\n");
            exit (1);
        }
        
    }
    
    return;
    
}

short locktab_get_sem(void)
{
    int tries;
    struct sembuf s = {0, -1, 0};

    for (tries = 0; tries < 5; tries++) {

        if (semop (semid_locktab, &s, 1) != -1) {
            return TRUE;
        }

        sleep (1);

    }
    
    return FALSE;
}

void locktab_release_sem(void)
{
    struct sembuf s = {0, 1, 0};

    semop (semid_locktab, &s, 1);

}

void lock(char *lockarg, long time_out, char type)
{
    char *key = &(lockarg[1]);
    char a = lockarg[0];
    
    if (shm_config == NULL) {
        fprintf (stderr, "lock:  global LOCK operation attemped before shared memory available.\r\n");
        return;
    }

    switch (a) {

        case '+':
            locktab_increment (key, time_out, FALSE);
            break;
            
        case '-':
            locktab_decrement (key, time_out);
            break;
            
        case SP:
            locktab_unlock_all ();
            locktab_increment (key, time_out, TRUE);
            break;

        default:
            break;
            
    }

    return;
    
}

void locktab_increment(char *key, long lck_timeout, short old_lock)
{
    short lck_action;
    char chk_ns[256];
    int nref_ct = locktab_list_count (key);

    if (old_lock) {
        lck_action = lock_old;
    }
    else {
        lck_action = lock_inc;
    }

    strncpy (chk_ns, nsname, 256);
    
    switch (lck_timeout) {
            
        case -1: /* blocking lock (no timeout) */

            if (nref_ct > 1) {
                /* this is a lock list */
                char *nref;
                char tmps[255];

                int i;        
                int successes = 0;
                int attempts = 0;
                int list_pos = 0;
                
                char *attempt_status = (char *) malloc (nref_ct * sizeof (char));
                NULLPTRCHK(attempt_status,"locktab_increment");

                for (i = 0; i < nref_ct; i++) attempt_status[i] = (char) FALSE;
                
                stcpy (tmps, key);
                stcnv_m2c (tmps);                               
                
                nref = strtok (tmps, "\001\201");

                do {
                    
                    list_pos = 0;                    
                    attempts = 0;
                    successes = 0;
                    
                    for (;;) {                    

                        attempts++;
                       
                        if (attempt_status[list_pos] == FALSE) {
                            
                            if (locktab_insert (nref) != NULL) {
                                successes++;
                                attempt_status[list_pos] = TRUE;
                            }
                            else {
                                locktab_decrement (nref, -1L);
                                attempt_status[list_pos] = FALSE;
                            }

                        }
                        
                        nref = strtok (NULL, "\001\201");                    
                        if (nref == NULL) break;

                        list_pos++;
                        
                    }                        

                } while (successes < nref_ct);

                free (attempt_status);
                if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                return;
                
            }
            else {
                for (;;) {
                    
                    if (locktab_insert (key) != NULL) {
                        if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                        return;
                    }
                    else {
                        sleep (1);
                    }
                
                }
            }

            
        case 0: /* lock that returns immediately */

            if (nref_ct > 1) {
                /* this is a lock list */
                char *nref;
                char tmps[255];
                
                int successes = 0;
                int attempts = 0;
                
                stcpy (tmps, key);
                stcnv_m2c (tmps);                               
                
                nref = strtok (tmps, "\001\201");

                for (;;) {                    
                    attempts++;

                    if (locktab_insert (nref) != NULL) {
                        successes++;
                    }
                    else {
                        locktab_decrement (nref, 0L);
                        test = 0;
                        return;
                    }
                    
                    nref = strtok (NULL, "\001\201");                    
                    if (nref == NULL) break;
                }                        

                test = 1;
                if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);      
                return;
              
            }
            else {
            
                if (locktab_insert (key) != NULL) {
                    test = 1;
                    if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                }
                else {
                    test = 0;
                }

            }

            break;

        case 1: /* special case: lock with 1-second timeout */

            if (locktab_insert (key) != NULL) {
                test = 1;            
            }
            else {
                sleep (1);

                if (locktab_insert (key) != NULL) {
                    test = 1;
                    if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                }
                else {
                    test = 0;
                }
            }

            return;
            
        default: /* lock with timeout */
        {
            time_t start_secs;
            time_t end_secs;
            time_t elapsed;
            locktab_ent_t *lck = NULL;
            
            start_secs = time (NULL);

            if (nref_ct > 1) {
                /* this is a lock-list */
                printf ("lock-list with timeout\r\n");
            }
            else {
                
            
                for (;;) {
                    
                    
                    lck = locktab_insert (key);
                    
                    end_secs = time (NULL);
                    elapsed = end_secs - start_secs;
                    
                    if (lck != NULL) {
                        test = 1;
                        return;
                    }
                    
                    if (elapsed >= lck_timeout) {
                        
                        if (lck == NULL) {
                            test = 0;          
                        }
                        else {
                            if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                            test = 1;
                        }
                        
                        return;
                        
                    }

                    sleep (1); /* prevent CPU pegging */
                
                } /* timeout loop */

                return;
            }
            
        } /* lock with timeout */
    } /* switch (timeout) */
            
            
}

void locktab_decrement(char *key, long lck_timeout)
{

    locktab_ent_t *lck = locktab_find (key);
    
    if (lck != NULL) {

        if (tp_level > lck->tp_level) {
            merr_raise (M41);
            return;
        }
        
        if (lck->ct > 0) lck->ct--;       
        
        if (lck->ct == 0) {
            lck->owner_job = 0;
            strcpy (lck->namespace, "<REUSABLE>");

            ssvn_lock_remove (lck->nref);
        }

        if (lck->owner_job != 0) {
            ssvn_lock_add (lck->nref, lck->owner_job, lck->ct);
        }
        
    }

    if (lck_timeout > -1) test = 1;
    
}

void locktab_unlock_all(void)
{
    locktab_ent_t *lck;
    
    for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {

        if (lck->owner_job == pid) {

            if (tp_level > lck->tp_level) {
                merr_raise (M41);
                return;
            }
            
            lck->ct = 0;
            lck->owner_job = 0;
            strcpy (lck->namespace, "<REUSABLE>");

            ssvn_lock_remove (lck->nref);
            
        }

    }       
    
}

locktab_ent_t *locktab_find(char *key)
{

    locktab_ent_t *lck;
    char chk_ns[255];

    if (key[1] == '%') {
        snprintf (chk_ns, 255, "SYSTEM");
    }
    else {
        snprintf (chk_ns, 255, "%s", nsname);
    }

    for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {

        if ((stcmp (lck->nref, key) == 0) && (strcmp (lck->namespace, chk_ns) == 0)) {

            if (lck->owner_job != pid) {
                return (locktab_ent_t *) NULL;
            }
            else {
                return lck;
            }
            
        }
        
    }

    return (locktab_ent_t *) NULL;
    
}

locktab_ent_t *locktab_insert(char *key)
{
    locktab_ent_t *l;
    char chk_ns[255];

    freem_ref_t *ik;
    freem_ref_t *ok;
    
    ik = malloc (sizeof (freem_ref_t));
    NULLPTRCHK(ik,"locktab_insert");

    ik = mref_init (ik, MREF_RT_GLOBAL, "");

    ok = malloc (sizeof (freem_ref_t));
    NULLPTRCHK(ok,"locktab_insert");    

    ik = internal_to_mref (ik, key);
    
    if (key[1] == '%') {
        snprintf (chk_ns, 255, "SYSTEM");
    }
    else {
        snprintf (chk_ns, 255, "%s", nsname);
    }
    
    for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
        ok = mref_init (ok, MREF_RT_GLOBAL, "");
        ok = internal_to_mref (ok, l->nref);
        
        if (((stcmp (l->nref, key) == 0) || (mref_is_descendant (ok, ik) == TRUE)) && (strcmp (l->namespace, chk_ns) == 0)) {
            
            /* nref already owned by another job */
            if ((l->owner_job != pid) && (l->ct > 0)) {
                free (ik);
                free (ok);
                return NULL;
            }
            else {
                if ((mref_is_descendant (ok, ik)) && (pid == l->owner_job)) {
                    if (locktab_find (key) == NULL) goto new_insert;
                }
                
                /* increment the lock and return */
                l->ct++;

                /* if this was a lock with a counter of zero belonging to another pid,
                 * re-use it and take ownership of it.
                 */
                if (l->owner_job != pid) l->owner_job = pid;

                l->tp_level = tp_level;
                
                ssvn_lock_add (l->nref, l->owner_job, l->ct);

                free (ik);
                free (ok);
                
                return l;
            }
            
        }

    }

new_insert:
    /* no lock exists for key: this is a new insert */            
    l = (locktab_ent_t *) shm_alloc (sizeof (locktab_ent_t));
    if (l == (locktab_ent_t *) NULL) {
        free (ik);
        free (ok);
        return (locktab_ent_t *) NULL;
    }

    stcpy (l->nref, key);
    snprintf (l->namespace, 255, "%s", chk_ns);
        
    l->owner_job = pid;
    l->ct = 1;
    
    l->next = shm_config->hdr->locktab_head;
    shm_config->hdr->locktab_head = l;

    ssvn_lock_add (l->nref, l->owner_job, l->ct);

    free (ik);
    free (ok);
    
    return l;
}


int locktab_count(char *key)
{
    locktab_ent_t *l;
    int ct = 0;

    for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
        if (stcmp (l->nref, key) == 0) ct++;
    }

    return ct;
}

int locktab_list_count(char *key)
{
    int i;
    int lct = 0;
    
    for (i = 0; i < stlen (key); i++) {
        if (key[i] == '\001') lct++;        
    }

    return lct;
}

unsigned long locktab_pages(void)
{

    locktab_ent_t *l;
    unsigned long bytes = 0;
    unsigned long pages = 0;
    float extra;
    
    for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
        bytes += sizeof (locktab_ent_t);
    }

    pages = bytes / PG_SIZE;
    extra = bytes % PG_SIZE;
    
    if (extra > 0) {
        pages++;
    }

    return pages;
  
}

unsigned long locktab_bytes(void)
{

    locktab_ent_t *l;
    unsigned int ct = 0;
    unsigned long bytes = 0;

    for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
        ct++;
        bytes += sizeof (locktab_ent_t);
    }

    return bytes;

}

void locktab_dump(void)
{
    
    locktab_ent_t *l;
    unsigned long ct = 0;
    unsigned long tot = 0;
    freem_ref_t *r;
    char *ref_ext;
    
    r = (freem_ref_t *) malloc (sizeof (freem_ref_t));
    NULLPTRCHK(r,"locktab_dump");

    ref_ext = (char *) malloc (STRLEN * sizeof (char));
    NULLPTRCHK(ref_ext,"locktab_dump");
    
    printf ("%-20s%-20s%-20s%s\r\n", "NAMESPACE", "PID", "COUNT", "KEY");
    printf ("%-20s%-20s%-20s%s\r\n", "---------", "---", "-----", "---");

    if (shm_config->hdr->locktab_head == NULL) {
        printf ("\r\n*** lock table empty ***\r\n");
        free (r);
        return;
    }

    
    for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {

        mref_init (r, MREF_RT_GLOBAL, "");
        internal_to_mref (r, l->nref);
        mref_to_external (r, ref_ext);

        if (l->owner_job) {
            printf ("%-20s%-20d%-20d%s\r\n", l->namespace, l->owner_job, l->ct, ref_ext);
            ct++;
        }
        
        tot++;
        
    }

    printf ("\r\n\tActive LOCK table entries:            %ld\r\n", ct);
    printf (    "\tReusable LOCK table entries:          %ld\r\n", tot - ct);
    printf (    "\tShared memory pages:                  %ld\r\n", locktab_pages ());
    printf (    "\tShared memory bytes:                  %ld\r\n", locktab_bytes ());

    free (r);
    free (ref_ext);
    
    return;
    
}

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