Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/trusty/trusty/user/base/lib/tipc/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 13 kB image not shown  

Quelle  tipc.c

  Sprache: C
 

/*
 * Copyright (C) 2018 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.
 */


#define TLOG_TAG "libtipc"

#include <lib/tipc/tipc.h>

#include <assert.h>
#include <lk/err_ptr.h>
#include <lk/macros.h>
#include <stdio.h>
#include <stdlib.h>
#include <trusty/time.h>
#include <trusty_log.h>
#include <uapi/err.h>

#include "tipc_priv.h"

int tipc_connect(handle_t* handle_p, const char* port) {
    int rc;

    assert(handle_p);

    rc = connect(port, IPC_CONNECT_WAIT_FOR_PORT);
    if (rc < 0)
        return rc;

    *handle_p = (handle_t)rc;
    return 0;
}

/*
 *  Send single buf message
 */

int tipc_send1(handle_t chan, const void* buf, size_t len) {
    struct iovec iov = {
            .iov_base = (void*)buf,
            .iov_len = len,
    };
    ipc_msg_t msg = {
            .iov = &iov,
            .num_iov = 1,
            .handles = NULL,
            .num_handles = 0,
    };
    return send_msg(chan, &msg);
}

/*
 *  Receive single buf message
 */

int tipc_recv1(handle_t chan, size_t min_sz, void* buf, size_t buf_sz) {
    int rc;
    ipc_msg_info_t msg_inf;

    rc = get_msg(chan, &msg_inf);
    if (rc)
        return rc;

    if (msg_inf.len < min_sz || msg_inf.len > buf_sz) {
        /* unexpected msg size: buffer too small or too big */
        rc = ERR_BAD_LEN;
    } else {
        struct iovec iov = {
                .iov_base = buf,
                .iov_len = buf_sz,
        };
        ipc_msg_t msg = {
                .iov = &iov,
                .num_iov = 1,
                .handles = NULL,
                .num_handles = 0,
        };
        rc = read_msg(chan, msg_inf.id, 0, &msg);
    }

    put_msg(chan, msg_inf.id);
    return rc;
}

/*
 * Send message consisting of two segments (header and payload)
 */

int tipc_send2(handle_t chan,
               const void* hdr,
               size_t hdr_len,
               const void* payload,
               size_t payload_len) {
    struct iovec iovs[2] = {
            {
                    .iov_base = (void*)hdr,
                    .iov_len = hdr_len,
            },
            {
                    .iov_base = (void*)payload,
                    .iov_len = payload_len,
            },
    };
    ipc_msg_t msg = {
            .iov = iovs,
            .num_iov = countof(iovs),
            .handles = NULL,
            .num_handles = 0,
    };
    return send_msg(chan, &msg);
}

/*
 * Receive message consisting of two segments.
 */

int tipc_recv2(handle_t chan,
               size_t min_sz,
               void* buf1,
               size_t buf1_sz,
               void* buf2,
               size_t buf2_sz) {
    int rc;
    ipc_msg_info_t msg_inf;

    rc = get_msg(chan, &msg_inf);
    if (rc)
        return rc;

    if (msg_inf.len < min_sz || (msg_inf.len > (buf1_sz + buf2_sz))) {
        /* unexpected msg size: buffer too small or too big */
        rc = ERR_BAD_LEN;
    } else {
        struct iovec iovs[2] = {
                {
                        .iov_base = buf1,
                        .iov_len = buf1_sz,
                },
                {
                        .iov_base = buf2,
                        .iov_len = buf2_sz,
                },
        };
        ipc_msg_t msg = {
                .iov = iovs,
                .num_iov = countof(iovs),
                .handles = NULL,
                .num_handles = 0,
        };
        rc = read_msg(chan, msg_inf.id, 0, &msg);
    }

    put_msg(chan, msg_inf.id);
    return rc;
}

/*
 * Handle common unexpected port events
 */

void tipc_handle_port_errors(const struct uevent* ev) {
    if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
        (ev->event & IPC_HANDLE_POLL_HUP) ||
        (ev->event & IPC_HANDLE_POLL_MSG) ||
        (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
        /* should never happen with port handles */
        TLOGE("error event (0x%x) for port (%d)\n", ev->event, ev->handle);
        abort();
    }
}

/*
 * Handle common unexpected channel events
 */

void tipc_handle_chan_errors(const struct uevent* ev) {
    if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
        (ev->event & IPC_HANDLE_POLL_READY)) {
        /* should never happen for channel handles */
        TLOGE("error event (0x%x) for chan (%d)\n", ev->event, ev->handle);
        abort();
    }
}

/*
 * Initialize an existing tipc_hset
 */

int tipc_hset_init(struct tipc_hset* hset) {
    int rc;

    assert(!IS_ERR(hset) && hset);

    hset->handle = INVALID_IPC_HANDLE;
    hset->work_queue = (struct list_node)LIST_INITIAL_VALUE(hset->work_queue);

    rc = handle_set_create();
    if (rc < 0)
        return rc;

    hset->handle = (handle_t)rc;
    return 0;
}

/*
 * Allocate and initialize new handle set structure
 */

struct tipc_hset* tipc_hset_create(void) {
    struct tipc_hset* hset;

    hset = malloc(sizeof(struct tipc_hset));
    if (!hset)
        return (void*)(uintptr_t)(ERR_NO_MEMORY);

    int rc = tipc_hset_init(hset);
    if (rc < 0) {
        free(hset);
        return (void*)(uintptr_t)(rc);
    }

    return hset;
}

/*
 * Add handle to handle set
 */

int tipc_hset_add_entry(struct tipc_hset* hset,
                        handle_t handle,
                        uint32_t evt_mask,
                        struct tipc_event_handler* evt_handler) {
    struct uevent uevt = {
            .handle = handle,
            .event = evt_mask,
            .cookie = (void*)evt_handler,
    };

    if (IS_ERR(hset) || !hset || !evt_handler)
        return ERR_INVALID_ARGS;

    assert(evt_handler->proc);

    /* attach new entry */
    return handle_set_ctrl(hset->handle, HSET_ADD, &uevt);
}

/*
 * Modify handle set entry
 */

int tipc_hset_mod_entry(struct tipc_hset* hset,
                        handle_t handle,
                        uint32_t evt_mask,
                        struct tipc_event_handler* evt_handler) {
    struct uevent uevt = {
            .handle = handle,
            .event = evt_mask,
            .cookie = (void*)evt_handler,
    };

    if (IS_ERR(hset) || !hset || !evt_handler)
        return ERR_INVALID_ARGS;

    assert(evt_handler->proc);

    /* modify entry */
    return handle_set_ctrl(hset->handle, HSET_MOD, &uevt);
}

/*
 * Remove handle from handle set
 */

int tipc_hset_remove_entry(struct tipc_hset* hset, handle_t h) {
    struct uevent uevt = {
            .handle = h,
            .event = 0,
            .cookie = NULL,
    };

    if (IS_ERR(hset) || !hset)
        return ERR_INVALID_ARGS;

    /* detach entry */
    return handle_set_ctrl(hset->handle, HSET_DEL, &uevt);
}

#define MS_NS_FACTOR 1000000

static uint32_t ms_until_deadline(int64_t deadline_at, int64_t curr_time) {
    if (deadline_at == INT64_MAX) {
        return INFINITE_TIME;
    }
    if (deadline_at <= curr_time) {
        return 0;
    }

    int64_t to_deadline_ns = deadline_at - curr_time;
    int64_t to_deadline_ms = DIV_ROUND_UP(to_deadline_ns, MS_NS_FACTOR);
    if (to_deadline_ms >= (int64_t)INFINITE_TIME) {
        /*
         * The deadline is further away than will fit in a uint32; do a wait
         * that's as long as possible, but not INFINITE_TIME.
         */

        return INFINITE_TIME - 1;
    }

    return to_deadline_ms;
}

/**
 * calc_timeout() - Calculates the timeout to use to wait() for events
 * @timeout_at:   the system time (ns) before tipc_handle_event() should return
 * @next_work_at: the system time (ns) at which the next work queue item should
 *                be handled
 * @curr_time:    the current system time (ns)
 *
 * Return: the timeout to pass to wait()
 */

static uint32_t calc_timeout(int64_t timeout_at,
                             int64_t next_work_at,
                             int64_t curr_time) {
    if (timeout_at < next_work_at) {
        return ms_until_deadline(timeout_at, curr_time);
    }
    return ms_until_deadline(next_work_at, curr_time);
}

static int tipc_handle_work_queue(struct tipc_hset* hset,
                                  int64_t current_time,
                                  int64_t* next_work_time) {
    *next_work_time = INT64_MAX;

    /*
     * The list is in decreasing run_after order, so pop elements from the tail
     * as they're processed. The order is decreasing to simplify the enqueue
     * code; see the explanation in tipc_queue_work_impl below.
     */

    struct tipc_work_todo* todo;
    while ((todo = list_peek_tail_type(&hset->work_queue, struct tipc_work_todo,
                                       node)) != NULL) {
        if (todo->run_after > current_time) {
            /* This todo isn't ready, so neither are any after it. */
            *next_work_time = todo->run_after;
            break;
        }

        list_delete(&todo->node);
        int rc = todo->do_work(todo);
        if (rc != NO_ERROR) {
            TLOGE("failed to handle queued work item\n");
            return rc;
        }
    }

    return NO_ERROR;
}

int tipc_handle_event(struct tipc_hset* hset, uint32_t timeout) {
    int rc;
    struct uevent evt = UEVENT_INITIAL_VALUE(evt);

    if (IS_ERR(hset) || !hset)
        return ERR_INVALID_ARGS;

    int64_t curr_time;
    rc = trusty_gettime(0, &curr_time);
    if (rc != NO_ERROR) {
        TLOGE("failed to gettime\n");
        return rc;
    }
    int64_t timeout_at =
            timeout == INFINITE_TIME
                    ? INT64_MAX
                    : curr_time + MS_NS_FACTOR * ((int64_t)timeout);

    do {
        int64_t next_work_time;
        rc = tipc_handle_work_queue(hset, curr_time, &next_work_time);
        if (rc != NO_ERROR) {
            return rc;
        }

        rc = trusty_gettime(0, &curr_time);
        if (rc != NO_ERROR) {
            TLOGE("failed to gettime\n");
            return rc;
        }

        /* wait for next event up to specified time */
        rc = wait(hset->handle, &evt,
                  calc_timeout(timeout_at, next_work_time, curr_time));
        if (rc != ERR_TIMED_OUT) {
            if (rc == NO_ERROR) {
                /* get handler */
                struct tipc_event_handler* handler = evt.cookie;

                /* invoke it */
                handler->proc(&evt, handler->priv);
            }
            /* got an event (even if it was an error), so return */
            return rc;
        }

        rc = trusty_gettime(0, &curr_time);
        if (rc != NO_ERROR) {
            TLOGE("failed to gettime\n");
            return rc;
        }

    } while (curr_time < timeout_at);

    /* Waited full duration w/o an event; give up. */
    return ERR_TIMED_OUT;
}

int tipc_run_event_loop(struct tipc_hset* hset) {
    int rc;

    do {
        rc = tipc_handle_event(hset, INFINITE_TIME);
    } while (rc == 0);

    assert(rc != ERR_TIMED_OUT);
    return rc;
}

void tipc_queue_work_delayed_abs(struct tipc_hset* hset,
                                 struct tipc_work_todo* todo,
                                 int64_t run_after_ns) {
    todo->run_after = run_after_ns;

    /*
     * Put work_queue in descending order because we expect most of the todos
     * callers add to have a run_after value that's greater than or equal to the
     * median run_after already in the queue, so we want to iterate though the
     * largest values first. There isn't a helper macro that iterates through
     * the list from tail to head, so we have to use descending order and pop
     * elements from the tail as we run them.
     *
     * We want to start iterating with the largest run_after values for
     * performace reasons. Callers adding multiple todos with the same run_after
     * (like if they calculate current_time + delay once and use it for each
     * todo) or increasing run_after values (like if they recalculate
     * current_time + delay for each todo) will be O(1) this way. Decreasing
     * run_after values (which would be O(N) enqueues) seem less likely.
     */

    struct tipc_work_todo* existing;
    list_for_every_entry(&hset->work_queue, existing, struct tipc_work_todo,
                         node) {
        if (todo->run_after >= existing->run_after) {
            list_add_before(&existing->node, &todo->node);
            return;
        }
    }
    list_add_tail(&hset->work_queue, &todo->node);
}

void tipc_queue_work(struct tipc_hset* hset, struct tipc_work_todo* todo) {
    tipc_queue_work_delayed_abs(hset, todo, 0);
}

void tipc_queue_work_delayed_rel(struct tipc_hset* hset,
                                 struct tipc_work_todo* todo,
                                 int64_t ns_from_now) {
    int64_t current_time;
    int rc = trusty_gettime(0, ¤t_time);
    /* trusty_gettime only fails when passed invalid args */
    assert(rc == NO_ERROR);

    tipc_queue_work_delayed_abs(hset, todo, current_time + ns_from_now);
}

void tipc_cancel_work(struct tipc_work_todo* todo) {
    list_delete(&todo->node);
}

Messung V0.5 in Prozent
C=90 H=89 G=89

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-06-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.