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

Define read, write, close, poll and ppoll with parameters #30

Closed
cglogic opened this issue May 12, 2021 · 9 comments
Closed

Define read, write, close, poll and ppoll with parameters #30

cglogic opened this issue May 12, 2021 · 9 comments

Comments

@cglogic
Copy link

cglogic commented May 12, 2021

Defining close and friends without arguments has border cases with include order.

Maybe it has a sense to change the definitions from #define close epoll_shim_close to #define close(fd) epoll_shim_close(fd)? But this change breaks function pointer assignments. What do you think? I can create pull request this change has sense for you.

Please see for details https://codeberg.org/dnkl/foot/issues/500#issuecomment-197764

@cglogic cglogic changed the title Define of read, write, close, poll and ppoll with parameters Define read, write, close, poll and ppoll with parameters May 12, 2021
@jiixyj
Copy link
Owner

jiixyj commented May 12, 2021

Sounds reasonable to me. Yeah, the current design has that problem with structs that contain members called close, read etc.

So the downside with function like macros is that this code no longer works for "epoll-like" fds, right?

#define close(fd) epoll_shim_close(fd)
int (*close_fp)(int) = &close;
close_fp(some_fd);

I am not aware of any consumers of epoll-shim using that pattern, but now I am aware of consumers using .close/.read in designated initializers.

I really don't want that valid code written for Linux that uses epoll needs any source changes to work with epoll-shim. The goal should be that most code "just works" when ported over to *BSD. Of course that won't always work perfectly, but I think we can get pretty close.

@dnkl
Copy link

dnkl commented May 12, 2021

So the downside with function like macros is that this code no longer works for "epoll-like" fds, right?

That would be correct.

I assume you have read the issue linked by @cglogic (since your example looks like it was copied from there)?

I just wanted to push the fact that we (foot) have worked around this and by no means depend on #31. It would be a nice-to-have, but I have no idea whether it would break other applications :)

@cglogic
Copy link
Author

cglogic commented May 12, 2021

If there are no known consumers that work with pointers to functions this can help in porting software like foot.

I'm not sure what is the best solution here.

@jiixyj
Copy link
Owner

jiixyj commented May 18, 2021

Another fun thing I noticed while playing around with this is that C's function like macros don't interact well with compound literals, so something like this errors out: ppoll(&pfd, 1, &(struct timespec) { 0, 0 }, &emptyset)

I'm now looking at defining the wrapper macros like this: #define ppoll(...) epoll_shim_ppoll(__VA_ARGS__). This seems to do the trick.

I can think of two more problems with that approach, but luckily there are workarounds:

  1. Actually calling a function pointer that has a conflicting name. You can fix this by wrapping the reference to the function pointer in parentheses:
	int fd;

	struct conflict conflict = {
		.close = conflict_close,
		.read = conflict_read,
	};

	(conflict.close)(fd);
  1. Overriding standard library functions that have a conflicting name. For example, you can replace the global read function like this:
#define NO_MACRO_EXPAND /**/

ssize_t
read NO_MACRO_EXPAND(int fd, void *buf, size_t count)
{
	...
}

The preprocessor will detect another token between the read and the ( and thus won't expand the function-like read macro.

@cglogic
Copy link
Author

cglogic commented May 18, 2021

Another fun thing I noticed while playing around with this is that C's function like macros don't interact well with compound literals, so something like this errors out: ppoll(&pfd, 1, &(struct timespec) { 0, 0 }, &emptyset)

I'm now looking at defining the wrapper macros like this: #define ppoll(...) epoll_shim_ppoll(__VA_ARGS__). This seems to do the trick.

Nice catch. Dealing with all cases is a bit tricky.

@cglogic
Copy link
Author

cglogic commented May 23, 2021

Found that #31 break building multimedia/v4l-utils on FreeBSD.

@jiixyj
Copy link
Owner

jiixyj commented May 23, 2021

Is it because of std::istreams .close() member? What a bummer...

Luckily, such usages of .close() can be relatively easily worked around with some automated patching in the BSD ports file like this:

echo "    stream.close()" | sed -e 's|\([^[:space:]]*\.close\)(|(\1)(|g'

In addition, I guess epoll-shim should give porters and "epoll-shim aware" consumers some means of controlling if/when those wrapper macros are defined, because it's not possible to cover all cases as we've seen.

For example, in this case here, the close( wrapper macro would stomp over the internal close usages in <fstream>:

#include <sys/epoll.h>

#include <fstream>
#include <iostream>

int main() {
        std::ifstream f;
        f.close();

        int ep = epoll_create1(0);
        close(ep);
}

We could have some way of deferring the definition of the wrapper macros, for example like this:

#define EPOLL_SHIM_NO_WRAPPER_MACROS
#include <sys/epoll.h>

#include <fstream>
#include <iostream>

#include <epoll-shim/wrapper-macros.h>

int main() {
        std::ifstream f;
        (f.close)();

        int ep = epoll_create1(0);
        close(ep);
}

Even the insertion of the #define EPOLL_SHIM_NO_WRAPPER_MACROS and #include <epoll-shim/wrapper-macros.h> lines could be automated with some kind of sed magic in the ports file, so no code changes would be needed:

  • #define EPOLL_SHIM_NO_WRAPPER_MACROS could be the first line of the file
  • #include <epoll-shim/wrapper-macros.h> would be the first line after the last #include line of the original file

@cglogic
Copy link
Author

cglogic commented Jun 15, 2021

Sorry for the delay.

After adding parameters for the macro close there is a situation where declaration of pointer to a function named close not touched but calls of close changed to epoll_shim_close.

The error:

In file included from ../../utils/common/cv4l-helpers.h:13:
../../utils/common/v4l-helpers.h:576:5: error: no member named 'epoll_shim_close' in 'v4l_fd'
        f->close(f);
        ~  ^
/usr/local/include/libepoll-shim/epoll-shim/detail/common.h:8:20: note: expanded from macro 'close'
#define close(...) epoll_shim_close(__VA_ARGS__)

v4l-helpers.h contains:

struct v4l_fd {
   ...
   int (*close)(struct v4l_fd *f);
   ...
};

@jiixyj
Copy link
Owner

jiixyj commented Jun 7, 2022

For corner cases like the above, the library now provides read/write/close/poll/ppoll/fcntl symbols in libepoll-shim-interpose.so.0. There is a new pkg-config file epoll-shim-interpose.pc for this mode. When using this, no macro redefinitions take place which should be much more robust.

Closing this for now.

@jiixyj jiixyj closed this as completed Jun 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants