Search code examples
cmultithreadingoperating-systemfunction-pointerspintos

C unable to recognise function pointer types as the same


In a recent project, I've been struggling to fix this syntax error for the last hour or so; gcc complains about a type mismatch between what it says are two objects of identical type:

../../devices/timer.c: In function ‘timer_interrupt’:
../../devices/timer.c:181:19: warning: passing argument 1 of ‘thread_foreach’ from incompatible pointer type [-Wincompatible-pointer-types]
  181 |   thread_foreach (timer_check, NULL);
      |                   ^~~~~~~~~~~
      |                   |
      |                   void (*)(struct thread *, void *)
In file included from ../../devices/timer.c:9:
../../threads/thread.h:134:42: note: expected ‘void (*)(struct thread *, void *)’ but argument is of type ‘void (*)(struct thread *, void *)’
  134 | void thread_foreach (thread_action_func *func, void *aux);
      |                      ~~~~~~~~~~~~~~~~~~~~^~~~
../../devices/timer.c: At top level:
../../devices/timer.c:185:1: error: conflicting types for ‘timer_check’
  185 | timer_check (struct thread *thread, void *aux) {
      | ^~~~~~~~~~~
In file included from ../../devices/timer.c:1:
../../devices/timer.h:29:6: note: previous declaration of ‘timer_check’ was here
   29 | void timer_check(struct thread *thread, void *aux);
      |      ^~~~~~~~~~~

I've played around with things like adding / removing reference or dereference operators, changing some of the signatures for the functions involved to make them more similar to examples I've seen online.

For example, I have tried changing the signature of thread_action_func from typedef void thread_action_func to typedef void (*thread_action_func), and changing the usage of the type in function arguments from thread_action_func * to thread_action_func, but it either complains about the type passed no longer being a function or function pointer, or throws the same error about identical types being mismatched.

I have also tried adding an address-of in front of where the function thread_foreach calls timer_check as an argument, like thread_foreach(&timer_check,...), but the error remained the same as initially.

The relevant functions / prototypes / typedefs are:

thread.h:

struct thread
  {
    ...
    int64_t block_ticks;
    ...
  };

typedef void thread_action_func (struct thread *t, void *aux);
void thread_foreach (thread_action_func *func, void *aux);

thread.c:

void
thread_foreach (thread_action_func *func, void *aux)
{
  struct list_elem *e;

  ASSERT (intr_get_level () == INTR_OFF);

  for (e = list_begin (&all_list); e != list_end (&all_list);
       e = list_next (e))
    {
      struct thread *t = list_entry (e, struct thread, allelem);
      func (t, aux);
    }
}

timer.h:

void timer_check(struct thread *thread, void *aux);

timer.c:

void
timer_sleep (int64_t ticks) 
{
  int64_t start = timer_ticks ();

  ASSERT (intr_get_level () == INTR_ON);

  struct thread *cur = thread_current();
  cur->block_ticks = ticks;
  enum intr_level old_level = intr_disable ();
  thread_block();

  intr_set_level (old_level);
  thread_yield();
}

static void
timer_interrupt (struct intr_frame *args UNUSED)
{
  ticks++;
  thread_tick ();
  thread_foreach (timer_check, NULL);
}

void 
timer_check (struct thread *thread, void *aux) {
  if (thread->status == THREAD_BLOCKED) {
    thread->block_ticks--;
    if (thread->block_ticks <= 0) {
      thread_unblock(thread);
    }
  }
}

All of the results I found by searching for this kind of problem were only similar in one aspect or the other, not close enough to be useful e.g. showing what the example of a function pointer is or an error with pointers to integers or characters.

I would guess this is some obvious syntax mistake which I was too frustrated to notice, but I can't really see clearly what may be causing the issue at the moment.


Solution

  • This error occurs when you redefine a struct tag in a different scope.

    You have not provided a Minimal Reproducible Example, so we cannot definitively identify where the error is, but it can be that this code in timer.h:

    void timer_check(struct thread *thread, void *aux);
    

    declares struct thread with function prototype scope. This defines a struct type only for the duration of the function declaration, which is generally useless.

    Then, in timer.c, presumably you include some header that declares thread_unblock, and this header is included after timer.h is included (if it is). That header declares its own struct thread and uses it in the declaration of thread_unblock.

    Since the function timer_check takes a parameter of a type that is different from the one used in thread_unblock, the compiler reports they have incompatible types, even though they have the same name (coming from different scopes).

    To fix that, timer.h should include the header that declares struct thread before it declares timer_check. Then the parameter declaration struct thread *thread will refer to the already visible structure type instead of defining a new one.

    For illustration, here is code that will reproduce the error:

    // struct thread; // Uncommenting this line will elimninate the error.
    
    void foo(struct thread *);
    void bar(void (*)(struct thread *));
    void baz(void)
    {
        bar(&foo);
    }