Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list: add a doubly linked list type. #19115

Closed
wants to merge 4 commits into from
Closed

Conversation

paulidale
Copy link
Contributor

@paulidale paulidale commented Sep 2, 2022

These list can be embedded into structures and structures can be members of
multiple lists. Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL; p = ossl_list_new_items_next(p))
        /* do something */
  • documentation is added or updated
  • tests are added or updated

@paulidale paulidale added the branch: master Merge to master branch label Sep 2, 2022
@paulidale paulidale self-assigned this Sep 2, 2022
These list can be embedded into structures and structures can be members of
multiple lists.  Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL;
         p = ossl_list_new_items_next(p))
        /* do something */
@paulidale paulidale added approval: review pending This pull request needs review by a committer approval: otc review pending triaged: feature The issue/pr requests/adds a feature labels Sep 2, 2022
@paulidale
Copy link
Contributor Author

If the size/complexity of the insertion and removal inline functions is deemed too big, they could be converted to wrappers around generic versions of the functions.

Copy link
Member

@levitte levitte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this silly. Even though we would use it primarly for QUIC, the implementation is so generic that it can be seen as such. No need to make it conditional like this.

test/recipes/02-test_list.t Outdated Show resolved Hide resolved
test/build.info Outdated Show resolved Hide resolved
test/build.info Outdated Show resolved Hide resolved
Copy link
Member

@t8m t8m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! Only a few copy&paste issues in the docs.

I agree with @levitte that this does not need to be disabled with quic. Especially because it is inlined so if not used by anything it won't raise the size of the libcrypto.

doc/internal/man3/DEFINE_LIST_OF.pod Outdated Show resolved Hide resolved
doc/internal/man3/DEFINE_LIST_OF.pod Outdated Show resolved Hide resolved
doc/internal/man3/DEFINE_LIST_OF.pod Outdated Show resolved Hide resolved
@paulidale
Copy link
Contributor Author

I did ask the question about needing to inline the insert/remove functions. They could easily be made generic and out of line. The rest might as well stay inlined since they're trivial.

@t8m
Copy link
Member

t8m commented Sep 2, 2022

I'd keep them inline for now.

@levitte levitte added approval: done This pull request has the required number of approvals and removed approval: review pending This pull request needs review by a committer labels Sep 2, 2022
@tmshort
Copy link
Contributor

tmshort commented Sep 2, 2022

Is this because we're assuming sys/queue.h isn't available everywhere?

@openssl-machine
Copy link
Collaborator

24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually.


OSSL_LIST(name);
OSSL_LIST_MEMBER(NAME, TYPE);
DEFINE_LIST_OF(NAME, TYPE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesnt this kind of break naming conventions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what sense?

test/list_test.c Outdated Show resolved Hide resolved
test/list_test.c Outdated Show resolved Hide resolved
Copy link
Member

@slontis slontis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How badly does it fall over if the user does something wrong?
Like double removal, or addition of a element already in the list..
These conditions could be easily checked and returned as errors by checking the next/prev pointers.

@paulidale
Copy link
Contributor Author

How badly does it fall over if the user does something wrong?

As it stands, it falls over really badly if bad stuff is done. As a low level internal data structure this is kind of acceptable. It would be possible to add extra checking in debug builds but not production ones.

How far should this go? E.g. it would be possible to automatically remove an item from a list when inserting it into another.

One programming error, which these kind of faults are, that can never be caught is uninitialised memory.

@slontis
Copy link
Member

slontis commented Sep 4, 2022

How badly does it fall over if the user does something wrong?

As it stands, it falls over really badly if bad stuff is done. As a low level internal data structure this is kind of acceptable. It would be possible to add extra checking in debug builds but not production ones.

How far should this go? E.g. it would be possible to automatically remove an item from a list when inserting it into another.

One programming error, which these kind of faults are, that can never be caught is uninitialised memory.

Checking if the next/prev are NULL is a fairly simple check that picks up most problems.. Yes if it is uninited then there is not much you can do.. Nice implementation by the way..

@paulidale
Copy link
Contributor Author

next/prev NULL will SEGV and be easy to find however.

@paulidale
Copy link
Contributor Author

paulidale commented Sep 4, 2022

The code can't check the element's next and free pointers: NULL is perfectly valid for either or both (single element list has both NULL). To do the kind of checks you're after requires keeping a current list pointer along with the next and prev pointers and checking that.

The only thing that can be checked is that the passed elements are not NULL.

@slontis
Copy link
Member

slontis commented Sep 5, 2022

If you insert a new node into a list - its next and prev should be NULL if it has been inited. Removing it should NULL the next and prev.

@paulidale
Copy link
Contributor Author

paulidale commented Sep 5, 2022

If you insert a new node into a list - its next and prev should be NULL if it has been inited.

This check isn't reliable, a node in a single element list will have next and prev equal to NULL.
If this were a public facing API, I agree that more validation is required but it's internal & where we don't expect such checking.

E.g. it is perfect legal to remove a node with two NULL links from a list.

BTW: there is no init function for nodes, just for the container.

@paulidale
Copy link
Contributor Author

I'd like this one to go in as it is.

We can figure out what manner of checking etc we want and I'll create a new PR handling that. I've raised an issue for this: #19129

@paulidale paulidale added approval: ready to merge The 24 hour grace period has passed, ready to merge and removed approval: done This pull request has the required number of approvals labels Sep 5, 2022
@paulidale
Copy link
Contributor Author

Is this because we're assuming sys/queue.h isn't available everywhere?

Yes, not in C89 😞

@paulidale
Copy link
Contributor Author

Merged, thanks for the reviews and feedback.

@paulidale paulidale closed this Sep 5, 2022
@paulidale paulidale deleted the list branch September 5, 2022 06:25
openssl-machine pushed a commit that referenced this pull request Sep 5, 2022
These list can be embedded into structures and structures can be members of
multiple lists.  Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL;
         p = ossl_list_new_items_next(p))
        /* do something */

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from #19115)
@levitte
Copy link
Member

levitte commented Sep 5, 2022

Is this because we're assuming sys/queue.h isn't available everywhere?

Yes, not in C89 disappointed

Not any C language level as far as I know, and not even POSIX. It's a BSD thing.

@hlandau
Copy link
Member

hlandau commented Sep 5, 2022

I wasn't around to review this, but this looks good to me. I actually had the same approach in mind. 👍

@paulidale paulidale restored the list branch September 12, 2022 01:05
sftcd pushed a commit to sftcd/openssl that referenced this pull request Sep 24, 2022
These list can be embedded into structures and structures can be members of
multiple lists.  Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL;
         p = ossl_list_new_items_next(p))
        /* do something */

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from openssl#19115)
@paulidale paulidale deleted the list branch October 11, 2022 06:28
beldmit pushed a commit to beldmit/openssl that referenced this pull request Dec 26, 2022
These list can be embedded into structures and structures can be members of
multiple lists.  Moreover, this is done without dynamic memory allocation.
That is, this is legal:

    typedef struct item_st ITEM;

    struct item_st {
        ...
        OSSL_LIST_MEMBER(new_items, ITEM);
        OSSL_LIST_MEMBER(failed_items, ITEM);
        ...
    };

    DEFINE_LIST_OF(new_items, TESTL);
    DEFINE_LIST_OF(failed_items, TESTL);

    struct {
        ...
        OSSL_LIST(new_items) new;
        OSSL_LIST(failed_items) failed;
        ...
    } *st;

    ITEM *p;

    for (p = ossl_list_new_items_head(&st->new); p != NULL;
         p = ossl_list_new_items_next(p))
        /* do something */

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from openssl#19115)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Merge to master branch triaged: feature The issue/pr requests/adds a feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants