summaryrefslogtreecommitdiff
path: root/kernel/pgrp.cc
blob: e5b382cf49dbb5c0855efb9e3ec07b944a04955c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

/*
 * Process Group / Session Implementation
 *
 * Hash table of signal_struct objects keyed by pgid.  Lifecycle of
 * signal_struct mirrors Linux: one per thread group, refcounted across
 * CLONE_THREAD siblings, freed when the last task in the group exits.
 *
 * Reference: Linux kernel/pid.c, kernel/exit.c
 */

#include <lume/pgrp.h>
#include <lume/signal_types.h>
#include <lume/task.h>
#include <lume/new.h>
#include <lume/klog.h>

/* ================================================================== */
/*  Hash table state                                                  */
/* ================================================================== */

list_node g_pgrp_hash[kPgrpHashSize];
Spinlock  g_pgrp_lock;

void pgrp_init()
{
    for (auto& bucket : g_pgrp_hash) bucket.init();
    g_pgrp_lock.init("pgrp");
}

/* ================================================================== */
/*  Internal helpers (must be called with g_pgrp_lock held)           */
/* ================================================================== */

static void pgrp_attach_locked(signal_struct* sig, int32 pgid)
{
    sig->pgid = pgid;
    list_node* bucket = &g_pgrp_hash[pgrp_hash_idx(pgid)];
    sig->pgrp_link.prev = bucket->prev;
    sig->pgrp_link.next = bucket;
    bucket->prev->next = &sig->pgrp_link;
    bucket->prev = &sig->pgrp_link;
}

static void pgrp_detach_locked(signal_struct* sig)
{
    /* Unlink only if currently on a list — init() makes prev/next point
     * to self, which we treat as "not linked". */
    if (sig->pgrp_link.next == &sig->pgrp_link) return;
    sig->pgrp_link.prev->next = sig->pgrp_link.next;
    sig->pgrp_link.next->prev = sig->pgrp_link.prev;
    sig->pgrp_link.init();
}

/* ================================================================== */
/*  signal_struct allocation / refcount                               */
/* ================================================================== */

signal_struct* alloc_signal_struct(int32 leader_pid, TaskControlBlock* leader)
{
    auto* sig = new signal_struct;
    if (!sig) return nullptr;
    sig->init(leader_pid, leader);

    LockGuard guard(g_pgrp_lock);
    pgrp_attach_locked(sig, leader_pid);
    return sig;
}

signal_struct* fork_signal_struct(signal_struct* parent,
                                  TaskControlBlock* child_leader)
{
    if (!parent) return nullptr;

    auto* sig = new signal_struct;
    if (!sig) return nullptr;

    /* Copy identity from parent, then override leader and attach. */
    sig->lock.init("signal");
    sig->refcount     = 1;
    sig->leader       = child_leader;
    sig->tty_old_pgrp = 0;
    sig->pgrp_link.init();

    {
        LockGuard pg(parent->lock);
        sig->pgid = parent->pgid;
        sig->sid  = parent->sid;
        sig->tty  = parent->tty;
    }

    LockGuard guard(g_pgrp_lock);
    pgrp_attach_locked(sig, sig->pgid);
    return sig;
}

void get_signal_struct(signal_struct* sig)
{
    if (!sig) return;
    LockGuard guard(sig->lock);
    sig->refcount++;
}

void put_signal_struct(signal_struct* sig)
{
    if (!sig) return;

    uint32 remaining;
    {
        LockGuard guard(sig->lock);
        remaining = --sig->refcount;
    }
    if (remaining != 0) return;

    /* Last reference — detach from pgrp hash and free. */
    {
        LockGuard guard(g_pgrp_lock);
        pgrp_detach_locked(sig);
    }
    delete sig;
}

/* ================================================================== */
/*  Pgrp membership change                                            */
/* ================================================================== */

void change_pgid(signal_struct* sig, int32 new_pgid)
{
    if (!sig) return;
    LockGuard guard(g_pgrp_lock);
    if (sig->pgid == new_pgid) return;
    pgrp_detach_locked(sig);
    pgrp_attach_locked(sig, new_pgid);
}

int pgrp_member_count(int32 pgid)
{
    LockGuard guard(g_pgrp_lock);
    int n = 0;
    list_node* bucket = &g_pgrp_hash[pgrp_hash_idx(pgid)];
    for (list_node* node = bucket->next; node != bucket; node = node->next) {
        auto* sig = list_entry<signal_struct, &signal_struct::pgrp_link>(node);
        if (sig->pgid == pgid) n++;
    }
    return n;
}