Hello everyone, I am creating an IDE in C using GTK+. In my IDE, i want to determine whenever the file accessed by another program. For that I used inotify() for POSIX. To determine whether the file is accessed or not, an infinite loop has to executed, which will continuously check whether file has been accessed or not. But, we know running an infinite loop in the main thread will block it. So, I decide to run the loop in another thread and whenever the file will be accessed a message box will be displayed. For that what I did is, I created a struct WatchFile which will watch the file whether it has been accessed or not. Here's the code.
watchfile.h
Code:
#include<sys/inotify.h>
#include<limits.h>
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
struct _WatchFile
{
int inotifyFd;
int *wd_array;
char **filename_array;
int files_count;
struct inotify_event *event;
pthread_t begin_watching_thread;
void (*callback_function)(void *watch_file,char *filename);
};
typedef struct _WatchFile WatchFile;
void watch_file_init(WatchFile *watch_file_object);
void watch_file_add_file(WatchFile *watch_file_object, char *filename);
int watch_file_remove_file(WatchFile *watch_file_object, char *filename);
void watch_file_add_callback(WatchFile *watch_file_object, void (*func)(void *watch_file,char *filename));
void watch_file_begin(WatchFile *watch_file_object);
void watch_file_stop(WatchFile *watch_file_object);
watchfile.c
Code:
#include"watchfileobject.h"
void watch_file_init(WatchFile *watch_file_object)
{
watch_file_object->inotifyFd = inotify_init();
watch_file_object->files_count = 0;
watch_file_object->filename_array = malloc(1*sizeof(char *));
watch_file_object->wd_array = malloc(1*sizeof(int));
}
void watch_file_add_file(WatchFile *watch_file_object, char *filename) //Remember to add Events to Arguments also
{
if(watch_file_object->files_count>0)
{
watch_file_object->filename_array = realloc(watch_file_object->filename_array,(watch_file_object->files_count+1)*sizeof(char *));
watch_file_object->wd_array = realloc(watch_file_object->wd_array,(watch_file_object->files_count+1)*sizeof(int));
}
asprintf(&watch_file_object->filename_array[watch_file_object->files_count],"%s",filename);
watch_file_object->wd_array[watch_file_object->files_count] = inotify_add_watch(watch_file_object->inotifyFd,filename,IN_ALL_EVENTS);
watch_file_object->files_count++;
}
int watch_file_remove_file(WatchFile *watch_file_object, char *filename)
{
if(watch_file_object->files_count==0)
return -1;
int index,i;
for(index=0;index<watch_file_object->files_count;index++)
if(strcmp(watch_file_object->filename_array[index],filename)==0)
break;
if(index==watch_file_object->files_count+1)
return -1;
int retval = inotify_rm_watch(watch_file_object->inotifyFd,watch_file_object->wd_array[index]);
for(i=index;i<watch_file_object->files_count;i++)
{
watch_file_object->filename_array[i]= watch_file_object->filename_array[i+1];
watch_file_object->wd_array[i] = watch_file_object->wd_array[i+1];
}
watch_file_object->files_count--;
return retval;
}
void watch_file_add_callback(WatchFile *watch_file_object, void (*func)(void *watch_file,char *filename))
{
watch_file_object->callback_function = func;
}
void *watch_file_thread_begin(void *watch_file)
{
WatchFile *watch_file_object = (WatchFile *)watch_file;
char buf[1000];
char *p;
while(1)
{
int n = read(watch_file_object->inotifyFd,buf,1000);
for(p=buf;p<buf+n;)
{
watch_file_object->event = (struct inotify_event *)p;
if(watch_file_object->event->mask & IN_ACCESS)
{
int wd =watch_file_object->event->wd, index;
for(index=0;index<watch_file_object->files_count;index++)
if(watch_file_object->wd_array[index]==wd)
break;
watch_file_object->callback_function((void *)watch_file_object,watch_file_object->filename_array[index]);
}
p +=sizeof(struct inotify_event)+watch_file_object->event->len;
}
}
return NULL;
}
void watch_file_begin(WatchFile *watch_file_object)
{
int s;
s = pthread_create(&watch_file_object->begin_watching_thread,NULL,watch_file_thread_begin,watch_file_object);
}
void watch_file_stop(WatchFile *watch_file_object)
{
pthread_cancel(watch_file_object->begin_watching_thread);
}
Here, we have different functions. watch_file_add_callback will add a callback function, and will be called whenever the file has been accessed. watch_file_begin will create a new thread and will run watch_file_thread_begin function in the newly created thread using pthread_t in POSIX and whenever the file will be accessed it will execute the callback_function.
As I will use it in my IDE, so I created a new watch_file object, initializes it, starts it and add or remove files whenever a file is opened or saved or a tab is removed. But whenever the file is accessed it calls the callback_function and if I try to create a GtkDialog inside that function and run it using gtk_dialog_run() then program hangs. Here's the callback function
Code:
void watch_file_file_modified(void *watch_file, char *filepath)
{
///Just implement a message dialog
//I think the problem is with threads
watch_file_stop(&watch_file_object);
GtkWidget *dialog,*label,*hbox;
char *message, *filename;
filename = strrchr(filepath,'/');
filename++;
asprintf(&message,"File %s has been modified. Do you want to reload it?",filename);
dialog = gtk_dialog_new();
label = gtk_label_new(message);
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),label);
gtk_widget_show_all(dialog);
gtk_dialog_run(GTK_DIALOG(dialog));
free(message);
}
I think the problem is that the GtkDialog must run inside a main thread but it is running in another thread, so can you tell me how to solve this problem. I know we have GFileMonitor, but I want to solve this problem.