0

The problem I'm running into is that when the file tries to copy, it only copies part of the file and the other part is a bunch of unreadable characters. This is for an academic assignment that wants us to use barrier synchronization so we need to use open, write, and read.

I've reworked the thread function many times but if it's the problem I can change it again, I haven't changed the for loop in main at all so even that might be the problem but I don't know what it could be. Lastly, I don't really know what to do with the barrier; my professor was very vague and I can't really ask him questions, maybe the barrier is the part that I'm truly missing.

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>

typedef struct args {
   int fd;
   int copy;
   long int start;
   long int size;
}threadarg;

int barrier = 0;

int main(int argc, char *argv[])
{
   void usage(char *progname);
   int chkdst(char **argv);
   void die(char *why);
   long int filesize(char *srcpath);
   void buildpath(char *src, char *dst, char **dstpath);
   int isvalid(char *path, char *dst);
   void *dowork(void *arg);

   if (argc < 4) usage("a8");
   int workers, i;
   char *check; 
   workers = strtol(argv[3], &check, 10);
   if (!check) usage("a8");
   else if (!chkdst(&argv[2])) die ("DST not valid!");
   long int size = filesize(argv[1]);
   if (size == -1) die ("Could not find file size");
   char *dstpath; buildpath(argv[1], argv[2], &dstpath);
   if (!isvalid(argv[1], dstpath)) die ("scr not valid!");
   long int chunksize = size / workers;
   long int remainder = size % workers;
   int fd = open(argv[1], O_RDONLY);
   int copy = open(dstpath, O_CREAT | O_RDWR, 0644);
   if (fd < 0 || copy < 0) die("Fail to access or create files");

   barrier = workers;
   threadarg threadargs[workers];
   pthread_t threads[workers];

   for (i = 0; i < workers; i++)
   {
      threadargs[i].fd = fd;
      threadargs[i].copy = copy;

      threadargs[i].start = i * chunksize;
      if (i == workers - 1) 
         threadargs[i].size = chunksize + remainder;
      else 
         threadargs[i].size = chunksize;

      if (pthread_create(&threads[i], NULL, dowork, (void *) &threadargs[i]))
         die("Thread Creation Failure");
   }

   for (i = 0; i < workers; i++)
      pthread_join(threads[i], NULL);
}

void usage(char *progname)
{
   fprintf(stderr, "./%s srcpath dstpath workercount\n", progname);
   exit(0);
}

void die(char *why)
{
   fprintf(stderr, "Program Killed...\nReason: %s\n", why);
   exit(1);
}

long int filesize(char *srcpath)
{
   struct stat st;
   if(stat(srcpath, &st) != 0) return 0;
   return st.st_size;
}
/*
void domd5(char *path)
{

}
*/
void *dowork(void *arg)
{
   threadarg *args = (threadarg *)arg;
   int fd = args->fd, 
   copy = args->copy, rd;
   long int start = args->start, 
   size = args->size;
   char bufs[2048], *remains;
   lseek(fd, start, SEEK_SET);
   lseek(copy, start, SEEK_SET);
   printf("%d thread with offset %ldKB, reached barrier\n", (int) pthread_self(), start);
   barrier--;
   while (barrier > 0); 

   long int count = 0, remainder = 0, i;
   for (i = 0; i < size; i += 2048)
   {
      if (i + 2048 > size)
      {
         remainder = size - count;
         remains = malloc(remainder * sizeof(char));
         rd = read (fd, remains, sizeof(remains));
         if (write(copy, remains, rd) != rd)
            die("Error accessing  files during copy"); 
         count += remainder;
      }
      else
      {
         rd = read(fd, bufs, sizeof(bufs));
         if (write(copy, bufs, rd) != rd)
            die("Error accessing files during copy");  
         count += 2048;
      }    
   }

   pthread_exit(NULL);
}

/* Takes a single pointer, *argv, and passes it to isdir()
   to check if the directory exists. If isdir returns a 1 a
   1 is returned from this module. Otherwise, an error message
   is printed and a 0 is returned.
   Calls isdir().
   Called by main().
*/
int chkdst(char **argv)
{
   int isdir(char *path);

   if (isdir(*argv)) return 1;
   return 0;
}

/* Uses the Stat struct to construct a struct, sbuf,
   and uses stat() to obtain information from the file and
   write it to sbuf. Uses S_ISDIR() on sbuf.st_mode to see
   the mode of the file. A 1 is returned if the file is a
   directory otherwise a 0 is returned.
   Called by isvalid().
*/
int isdir(char *path)
{
   struct stat sbuf;
   if (stat(path, &sbuf)) return 0;
   return S_ISDIR(sbuf.st_mode);
}

/* Uses the Stat struct to construct a struct, sbuf,
   and uses stat() to obtain information from the file and
   write it to sbuf. Uses S_ISREG on sbuf.st_mode to see if
   the file is regular. A 1 is returned if the S_ISREG is true
   otherwise a 0 is returned.
   Called by isvalid().
*/
int isregular(char *path)
{
   struct stat sbuf;
   if (stat(path, &sbuf)) return 0;
   return S_ISREG(sbuf.st_mode);
}

/* Checks if the source path is a directory first, then if its
   a regular file return 0 if it is dir and if it isn't a regular
   file, then checks if the destionation path was created or if
   the file exist at the destination if either return 0, if none
   of these return 1.
   Calls isdir() and isregular().
   Called by copyfiles().
*/
int isvalid(char *path, char *dst)
{
   if (isdir(path))
   {
      return 0;
   }
   else if (!isregular(path))
   {
      return 0;
   }
   else if (dst == NULL)
   {
      return 0;
   }
   else if (isregular(dst))
   {
      return 0;
   }

   return 1;
}

/* Builds destination-path using strrchr() function from library,
   dstpath is null on error and defined otherwise. The src file has
   its original destination removed and replaced with the new one if
   it has a original destination on it otherwise it is just added to
   the end of the existing name of the file.
   Called by copyfiles().
*/
void buildpath(char *src, char *dst, char **dstpath)
{
   char *ptr;
   int n;
   ptr = strrchr(src, '/');

   if (ptr) n = strlen(dst) + strlen(ptr) + 2;
   else n = strlen(dst) + strlen(src) + 2;

   *dstpath = malloc(n);

   if (!dstpath) return;

   if (ptr)
   {
      strcpy(*dstpath, dst);
      strcat(*dstpath, ptr);
   }
   else
   {
      strcpy(*dstpath, dst);
      strcat(*dstpath, "/");
      strcat(*dstpath, src);
   }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
  • Naturally only one thread can be executing the seeks and copy at any given time since you're *sharing* the *one file descriptor*. You're *totally lacking any attempts at synchronization before the seeks*, and the synchronization after seek looks like all the threads are blocked until all the threads are at the same point ready to mess the contents of the file all simultaeously. – Antti Haapala -- Слава Україні Nov 24 '19 at 19:42
  • @AnttiHaapala Should I be syncronizing before they seek? I have no clue, so I changed the fact the program was only using one file descriptor for each file now each thread has its own file descriptor, this should allow me to have the threads writing to the same file at the same time? Does the barrier synchronization need to be the thing I attempt to get this to work correctly? now it has one line that's really long of unreadable characters. – Kevin Bell Nov 24 '19 at 21:36
  • If each file has its own file descriptors you do not need synchronizing.... – Antti Haapala -- Слава Україні Nov 24 '19 at 21:44
  • So then what could be wrong with the copying then that would make it seemingly overlap at places when it shouldn't I've checked the start and size of each thread over and over to make sure it is right but that's the only thing I could think of that would mess it up. @AnttiHaapala – Kevin Bell Nov 24 '19 at 22:07
  • Hmm, above I meant each *thread* having its own descriptors... You know what a barrier synchronization means? The problem is that if you have a single file descriptor, you must guarantee **mut**ual **ex**clusion, not have a barrier... – Antti Haapala -- Слава Україні Nov 24 '19 at 22:10
  • @AnttiHaapala Right, normally I would think you would block threads from copying while another one is and letting them copy after the one that was finished. But the problem I need to solve is doing it in parallel. Doing it this way is making them cause unexpected outcomes and my prof also says we need to use barrier synchronization but it doesn't feel like it fits here unless I'm missing something. – Kevin Bell Nov 25 '19 at 01:52
  • yea I do not know what they could've meant by use of barrier sync here – Antti Haapala -- Слава Україні Nov 25 '19 at 05:19

0 Answers0