package main import ( "encoding/json" "fmt" "io/ioutil" "log" "os" "path" "runtime" "sync" "srs.epita.fr/fic-server/libfic" ) type GenerateType int const ( GenPublic GenerateType = iota GenEvents GenTeams GenThemes ) type genStruct struct { Id string Type GenerateType } var parallelJobs = runtime.NumCPU() var genTeamQueue chan *fic.Team var genQueue chan genStruct var inQueueMutex sync.RWMutex var inGenQueue map[GenerateType]bool func init() { genTeamQueue = make(chan *fic.Team) genQueue = make(chan genStruct) inGenQueue = map[GenerateType]bool{} } func launchWorkers() { log.Println("Running with", parallelJobs, "worker(s)") for i := parallelJobs; i > 0; i-- { go consumer() } } func appendGenQueue(gs genStruct) { inQueueMutex.RLock() if v, ok := inGenQueue[gs.Type]; !ok || !v { inQueueMutex.RUnlock() inQueueMutex.Lock() inGenQueue[gs.Type] = true inQueueMutex.Unlock() genQueue <- gs } else { inQueueMutex.RUnlock() } } func consumer() { for { var id string var err error select { case gt := <-genTeamQueue: err = genTeamMyFile(gt) case gs := <-genQueue: id = gs.Id inQueueMutex.Lock() inGenQueue[gs.Type] = false inQueueMutex.Unlock() switch gs.Type { case GenPublic: err = genMyPublicFile() case GenEvents: err = genEventsFile() case GenTeams: err = genTeamsFile() case GenThemes: err = genThemesFile() } } if err != nil { log.Println(id, "[ERR] Unable to generate:", err) } } } // Generate issues.json for a given team func genTeamIssuesFile(team fic.Team) error { dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)) if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { return fmt.Errorf("%s is not a directory", dirPath) } if my, err := team.MyIssueFile(); err != nil { return err } else if j, err := json.Marshal(my); err != nil { return err } else if err = ioutil.WriteFile(path.Join(dirPath, "issues.json"), j, 0644); err != nil { return err } return nil } // Generate my.json and wait.json for a given team func genTeamMyFile(team *fic.Team) error { dirPath := path.Join(TeamsDir, fmt.Sprintf("%d", team.Id)) if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { return fmt.Errorf("%s is not a directory", dirPath) } if my, err := fic.MyJSONTeam(team, true); err != nil { return err } else if j, err := json.Marshal(my); err != nil { return err } else if err = ioutil.WriteFile(path.Join(dirPath, "my.json"), j, 0666); err != nil { return err } // Speed up generation when challenge is started if !ChStarted { if my, err := fic.MyJSONTeam(team, false); err != nil { return err } else if j, err := json.Marshal(my); err != nil { return err } else if err = ioutil.WriteFile(path.Join(dirPath, "wait.json"), j, 0666); err != nil { return err } } return nil } // Generate public my.json file func genMyPublicFile() error { dirPath := path.Join(TeamsDir, "public") if s, err := os.Stat(dirPath); os.IsNotExist(err) { os.MkdirAll(dirPath, 0777) } else if !s.IsDir() { return fmt.Errorf("%s is not a directory", dirPath) } if my, err := fic.MyJSONTeam(nil, true); err != nil { return err } else if j, err := json.Marshal(my); err != nil { return err } else if err = ioutil.WriteFile(path.Join(dirPath, "my.json"), j, 0666); err != nil { return err } os.Symlink("my.json", path.Join(dirPath, "wait.json")) if teams, err := fic.ExportTeams(true); err != nil { return err } else if j, err := json.Marshal(teams); err != nil { return err } else if err = ioutil.WriteFile(path.Join(dirPath, "teams.json"), j, 0666); err != nil { return err } return nil } // Generate general evemts.json file func genEventsFile() error { if evts, err := fic.GetLastEvents(); err != nil { return err } else if j, err := json.Marshal(evts); err != nil { return err } else if err = ioutil.WriteFile(path.Join(TeamsDir, "events.json"), j, 0666); err != nil { return err } return nil } // Generate general teams.json file func genTeamsFile() error { if teams, err := fic.ExportTeams(false); err != nil { return err } else if j, err := json.Marshal(teams); err != nil { return err } else if err = ioutil.WriteFile(path.Join(TeamsDir, "teams.json"), j, 0666); err != nil { return err } if teams, err := fic.ExportTeams(true); err != nil { return err } else if j, err := json.Marshal(teams); err != nil { return err } else if err = ioutil.WriteFile(path.Join(TeamsDir, "public", "teams.json"), j, 0666); err != nil { return err } return nil } // Generate general themes.json file func genThemesFile() error { if themes, err := fic.ExportThemes(); err != nil { return err } else if j, err := json.Marshal(themes); err != nil { return err } else if err = ioutil.WriteFile(path.Join(TeamsDir, "themes.json"), j, 0666); err != nil { return err } return nil } func genAll() { appendGenQueue(genStruct{Type: GenThemes}) appendGenQueue(genStruct{Type: GenTeams}) appendGenQueue(genStruct{Type: GenEvents}) appendGenQueue(genStruct{Type: GenPublic}) if teams, err := fic.GetActiveTeams(); err != nil { log.Println("Team retrieval error: ", err) } else { for _, team := range teams { myteam := team // team is reused, we need to create a new variable here to store the value genTeamQueue <- &myteam } } }